Loading services/core/java/com/android/server/vibrator/VibratorManagerService.java +89 −68 Original line number Original line Diff line number Diff line Loading @@ -61,6 +61,7 @@ import android.os.VibratorInfo; import android.os.vibrator.Flags; import android.os.vibrator.Flags; import android.os.vibrator.IVibrationSessionCallback; import android.os.vibrator.IVibrationSessionCallback; import android.os.vibrator.PrebakedSegment; import android.os.vibrator.PrebakedSegment; import android.os.vibrator.PrimitiveSegment; import android.os.vibrator.VibrationConfig; import android.os.vibrator.VibrationConfig; import android.os.vibrator.VibrationEffectSegment; import android.os.vibrator.VibrationEffectSegment; import android.os.vibrator.VibratorInfoFactory; import android.os.vibrator.VibratorInfoFactory; Loading Loading @@ -2672,7 +2673,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { CombinedVibration.ParallelCombination combination = CombinedVibration.ParallelCombination combination = CombinedVibration.startParallel(); CombinedVibration.startParallel(); while ("-v".equals(getNextOption())) { while ("-v".equals(getNextOption())) { int vibratorId = Integer.parseInt(getNextArgRequired()); int vibratorId = parseInt(getNextArgRequired(), "Expected vibrator id after -v"); combination.addVibrator(vibratorId, nextEffect()); combination.addVibrator(vibratorId, nextEffect()); } } runVibrate(commonOptions, combination.combine()); runVibrate(commonOptions, combination.combine()); Loading @@ -2684,7 +2685,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { CombinedVibration.SequentialCombination combination = CombinedVibration.SequentialCombination combination = CombinedVibration.startSequential(); CombinedVibration.startSequential(); while ("-v".equals(getNextOption())) { while ("-v".equals(getNextOption())) { int vibratorId = Integer.parseInt(getNextArgRequired()); int vibratorId = parseInt(getNextArgRequired(), "Expected vibrator id after -v"); combination.addNext(vibratorId, nextEffect()); combination.addNext(vibratorId, nextEffect()); } } runVibrate(commonOptions, combination.combine()); runVibrate(commonOptions, combination.combine()); Loading @@ -2709,7 +2710,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { private int runHapticFeedback() { private int runHapticFeedback() { CommonOptions commonOptions = new CommonOptions(); CommonOptions commonOptions = new CommonOptions(); int constant = Integer.parseInt(getNextArgRequired()); int constant = parseInt(getNextArgRequired(), "Expected haptic feedback constant id"); IBinder deathBinder = commonOptions.background ? VibratorManagerService.this IBinder deathBinder = commonOptions.background ? VibratorManagerService.this : mShellCallbacksToken; : mShellCallbacksToken; Loading Loading @@ -2757,12 +2758,13 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { if ("-a".equals(nextOption)) { if ("-a".equals(nextOption)) { hasAmplitude = true; hasAmplitude = true; } else if ("-w".equals(nextOption)) { } else if ("-w".equals(nextOption)) { delay = Integer.parseInt(getNextArgRequired()); delay = parseInt(getNextArgRequired(), "Expected delay millis after -w"); } } } } long duration = Long.parseLong(getNextArgRequired()); long duration = parseInt(getNextArgRequired(), "Expected one-shot duration millis"); int amplitude = hasAmplitude ? Integer.parseInt(getNextArgRequired()) int amplitude = hasAmplitude ? parseInt(getNextArgRequired(), "Expected one-shot amplitude") : VibrationEffect.DEFAULT_AMPLITUDE; : VibrationEffect.DEFAULT_AMPLITUDE; composition.addOffDuration(Duration.ofMillis(delay)); composition.addOffDuration(Duration.ofMillis(delay)); composition.addEffect(VibrationEffect.createOneShot(duration, amplitude)); composition.addEffect(VibrationEffect.createOneShot(duration, amplitude)); Loading Loading @@ -2837,8 +2839,10 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { while ((nextOption = getNextOption()) != null) { while ((nextOption = getNextOption()) != null) { switch (nextOption) { switch (nextOption) { case "-a" -> isAdvanced = true; case "-a" -> isAdvanced = true; case "-i" -> initialSharpness = Float.parseFloat(getNextArgRequired()); case "-i" -> initialSharpness = parseFloat(getNextArgRequired(), case "-r" -> repeat = Integer.parseInt(getNextArgRequired()); "Expected initial sharpness after -i"); case "-r" -> repeat = parseInt(getNextArgRequired(), "Expected repeat index after -r"); } } } } Loading @@ -2864,8 +2868,8 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { // nextArg is not a duration, finish reading. // nextArg is not a duration, finish reading. break; break; } } intensity = Float.parseFloat(getNextArgRequired()); intensity = parseFloat(getNextArgRequired(), "Expected envelope intensity"); sharpness = Float.parseFloat(getNextArgRequired()); sharpness = parseFloat(getNextArgRequired(), "Expected envelope sharpness"); builder.addControlPoint(intensity, sharpness, duration); builder.addControlPoint(intensity, sharpness, duration); pos++; pos++; } } Loading Loading @@ -2893,16 +2897,14 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { getNextArgRequired(); // consume "waveform" getNextArgRequired(); // consume "waveform" String nextOption; String nextOption; while ((nextOption = getNextOption()) != null) { while ((nextOption = getNextOption()) != null) { if ("-a".equals(nextOption)) { switch (nextOption) { hasAmplitudes = true; case "-a" -> hasAmplitudes = true; } else if ("-r".equals(nextOption)) { case "-f" -> hasFrequencies = true; repeat = Integer.parseInt(getNextArgRequired()); case "-c" -> isContinuous = true; } else if ("-w".equals(nextOption)) { case "-r" -> repeat = parseInt(getNextArgRequired(), delay = Integer.parseInt(getNextArgRequired()); "Expected repeat index after -r"); } else if ("-f".equals(nextOption)) { case "-w" -> delay = parseInt(getNextArgRequired(), hasFrequencies = true; "Expected delay millis after -w"); } else if ("-c".equals(nextOption)) { isContinuous = true; } } } } List<Integer> durations = new ArrayList<>(); List<Integer> durations = new ArrayList<>(); Loading @@ -2920,14 +2922,15 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { break; break; } } if (hasAmplitudes) { if (hasAmplitudes) { amplitudes.add( int amplitude = parseInt(getNextArgRequired(), "Expected waveform amplitude"); Float.parseFloat(getNextArgRequired()) / VibrationEffect.MAX_AMPLITUDE); amplitudes.add((float) amplitude / VibrationEffect.MAX_AMPLITUDE); } else { } else { amplitudes.add(nextAmplitude); amplitudes.add(nextAmplitude); nextAmplitude = 1 - nextAmplitude; nextAmplitude = 1 - nextAmplitude; } } if (hasFrequencies) { if (hasFrequencies) { frequencies.add(Float.parseFloat(getNextArgRequired())); frequencies.add( parseFloat(getNextArgRequired(), "Expected waveform frequency")); } } } } Loading Loading @@ -2986,27 +2989,37 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { if ("-b".equals(nextOption)) { if ("-b".equals(nextOption)) { shouldFallback = true; shouldFallback = true; } else if ("-w".equals(nextOption)) { } else if ("-w".equals(nextOption)) { delay = Integer.parseInt(getNextArgRequired()); delay = parseInt(getNextArgRequired(), "Expected delay millis after -w"); } } } } int effectId = Integer.parseInt(getNextArgRequired()); int effectId = parseInt(getNextArgRequired(), "Expected prebaked effect id"); composition.addOffDuration(Duration.ofMillis(delay)); composition.addOffDuration(Duration.ofMillis(delay)); composition.addEffect(VibrationEffect.get(effectId, shouldFallback)); composition.addEffect(VibrationEffect.get(effectId, shouldFallback)); } } private void addPrimitivesToComposition(VibrationEffect.Composition composition) { private void addPrimitivesToComposition(VibrationEffect.Composition composition) { getNextArgRequired(); // consume "primitives" getNextArgRequired(); // consume "primitives" String nextArg; while (peekNextArg() != null) { while ((nextArg = peekNextArg()) != null) { int delay = 0; int delay = 0; if ("-w".equals(nextArg)) { float scale = 1f; getNextArgRequired(); // consume "-w" int delayType = PrimitiveSegment.DEFAULT_DELAY_TYPE; delay = Integer.parseInt(getNextArgRequired()); nextArg = peekNextArg(); String nextOption; while ((nextOption = getNextOption()) != null) { if ("-s".equals(nextOption)) { scale = parseFloat(getNextArgRequired(), "Expected scale after -s"); } else if ("-o".equals(nextOption)) { delayType = VibrationEffect.Composition.DELAY_TYPE_RELATIVE_START_OFFSET; delay = parseInt(getNextArgRequired(), "Expected offset millis after -o"); } else if ("-w".equals(nextOption)) { delayType = PrimitiveSegment.DEFAULT_DELAY_TYPE; delay = parseInt(getNextArgRequired(), "Expected delay millis after -w"); } } } try { try { composition.addPrimitive(Integer.parseInt(nextArg), /* scale= */ 1, delay); String nextArg = peekNextArg(); // Just in case this is not a primitive. composition.addPrimitive(Integer.parseInt(nextArg), scale, delay, delayType); getNextArgRequired(); // consume the primitive id getNextArgRequired(); // consume the primitive id } catch (NumberFormatException | NullPointerException e) { } catch (NumberFormatException | NullPointerException e) { // nextArg is not describing a primitive, leave it to be consumed by outer loops // nextArg is not describing a primitive, leave it to be consumed by outer loops Loading @@ -3032,17 +3045,15 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { VibrationXmlParser.parseDocument(new StringReader(xml)); VibrationXmlParser.parseDocument(new StringReader(xml)); VibratorInfo combinedVibratorInfo = getCombinedVibratorInfo(); VibratorInfo combinedVibratorInfo = getCombinedVibratorInfo(); if (combinedVibratorInfo == null) { if (combinedVibratorInfo == null) { throw new IllegalStateException( throw new IllegalStateException("No vibrator info available to parse XML"); "No combined vibrator info to parse vibration XML " + xml); } } VibrationEffect effect = parsedVibration.resolve(combinedVibratorInfo); VibrationEffect effect = parsedVibration.resolve(combinedVibratorInfo); if (effect == null) { if (effect == null) { throw new IllegalArgumentException( throw new IllegalArgumentException("Parsed XML cannot be resolved: " + xml); "Parsed vibration cannot be resolved for vibration XML " + xml); } } return CombinedVibration.createParallel(effect); return CombinedVibration.createParallel(effect); } catch (IOException e) { } catch (IOException e) { throw new RuntimeException("Error parsing vibration XML " + xml, e); throw new RuntimeException("Error parsing XML: " + xml, e); } } } } Loading @@ -3060,16 +3071,30 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { } } } } private static int parseInt(String text, String errorMessage) { try { return Integer.parseInt(text); } catch (NumberFormatException | NullPointerException e) { throw new IllegalArgumentException(errorMessage, e); } } private static float parseFloat(String text, String errorMessage) { try { return Float.parseFloat(text); } catch (NumberFormatException | NullPointerException e) { throw new IllegalArgumentException(errorMessage, e); } } @Override @Override public void onHelp() { public void onHelp() { try (PrintWriter pw = getOutPrintWriter();) { try (PrintWriter pw = getOutPrintWriter();) { pw.println("Vibrator Manager commands:"); pw.println("Vibrator Manager commands:"); pw.println(" help"); pw.println(" help"); pw.println(" Prints this help text."); pw.println(" Prints this help text."); pw.println(""); pw.println(" list"); pw.println(" list"); pw.println(" Prints the id of device vibrators. This does not include any "); pw.println(" Prints device vibrator ids; does not include input devices."); 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(" Vibrates effect on all vibrators in sync."); pw.println(" combined [options] (-v <vibrator-id> <effect>...)..."); pw.println(" combined [options] (-v <vibrator-id> <effect>...)..."); Loading @@ -3079,51 +3104,41 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { pw.println(" xml [options] <xml>"); pw.println(" xml [options] <xml>"); pw.println(" Vibrates using combined vibration described in given XML string"); pw.println(" Vibrates using combined vibration described in given XML string"); pw.println(" on all vibrators in sync. The XML could be:"); pw.println(" on all vibrators in sync. The XML could be:"); pw.println(" XML containing a single effect, or"); pw.println(" A single <vibration-effect>, or"); pw.println(" A vibration select XML containing multiple effects."); pw.println(" A <vibration-select> containing multiple effects."); pw.println(" Vibrates using combined vibration described in given XML string."); pw.println(" feedback [options] <constant>"); pw.println(" XML containing a single effect it runs on all vibrators in sync."); pw.println(" Performs a haptic feedback with the given constant."); pw.println(" cancel"); pw.println(" cancel"); pw.println(" Cancels any active vibration"); pw.println(" Cancels any active vibration"); pw.println(" feedback [-f] [-d <description>] <constant>"); pw.println(" Performs a haptic feedback with the given constant."); pw.println(" The force (-f) option enables the `always` configuration, which"); pw.println(" plays the haptic irrespective of the vibration intensity settings"); pw.println(""); pw.println(""); pw.println("Effect commands:"); pw.println("Effect commands:"); pw.println(" oneshot [-w delay] [-a] <duration> [<amplitude>]"); pw.println(" oneshot [-w delay] [-a] <duration> [<amplitude>]"); pw.println(" Vibrates for duration milliseconds; ignored when device is on "); pw.println(" Vibrates for duration milliseconds."); 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(" If -w is provided, the effect will be played after the specified"); pw.println(" wait time in milliseconds."); pw.println(" wait time in milliseconds."); pw.println(" If -a is provided, the command accepts a second argument for "); pw.println(" If -a is provided, the command accepts a second argument for "); pw.println(" amplitude, in a scale of 1-255."); pw.println(" amplitude, in a scale of 1-255."); pw.print(" waveform [-w delay] [-r index] [-a] [-f] [-c] "); pw.print(" waveform [-w delay] [-r index] [-a] [-f] [-c] "); pw.println("(<duration> [<amplitude>] [<frequency>])..."); pw.println("(<duration> [<amplitude>] [<frequency>])..."); pw.println(" Vibrates for durations and amplitudes in list; ignored when "); pw.println(" Vibrates for durations and amplitudes in list."); 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(" If -w is provided, the effect will be played after the specified"); pw.println(" wait time in milliseconds."); pw.println(" wait time in milliseconds."); pw.println(" If -r is provided, the waveform loops back to the specified"); 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(" index (e.g. 0 loops from the beginning)."); pw.println(" If -a is provided, the command expects amplitude to follow each"); pw.println(" If -a is provided, the command expects amplitude to follow each"); pw.println(" duration; otherwise, it accepts durations only and alternates"); pw.println(" duration; otherwise, it accepts durations only and alternates"); pw.println(" off/on"); pw.println(" off/on."); pw.println(" If -f is provided, the command expects frequency to follow each"); pw.println(" If -f is provided, the command expects frequency to follow each"); pw.println(" amplitude or duration; otherwise, it uses resonant frequency"); pw.println(" amplitude or duration; otherwise, it uses resonant frequency."); pw.println(" If -c is provided, the waveform is continuous and will ramp"); pw.println(" If -c is provided, the waveform is continuous and will ramp"); pw.println(" between values; otherwise each entry is a fixed step."); pw.println(" between values; otherwise each entry is a fixed step."); pw.println(" Duration is in milliseconds; amplitude is a scale of 1-255;"); pw.println(" Duration is in milliseconds; amplitude is a scale of 1-255;"); pw.println(" frequency is an absolute value in hertz;"); pw.println(" frequency is an absolute value in hertz."); pw.print(" envelope [-a] [-i initial sharpness] [-r index] "); pw.print(" envelope [-a] [-i initial sharpness] [-r index] "); pw.println("[<duration1> <intensity1> <sharpness1>]..."); pw.println("[<duration1> <intensity1> <sharpness1>]..."); pw.println(" Generates a vibration pattern based on a series of duration, "); pw.println(" Generates a vibration pattern based on a series of duration, "); pw.println(" intensity, and sharpness values. The total vibration time is "); pw.println(" intensity, and sharpness values. The total vibration time is "); pw.println(" the sum of all durations; Ignored when device is on "); pw.println(" the sum of all durations."); pw.println(" DND (Do Not Disturb) mode; touch feedback strength user setting "); pw.println(" will be used to scale amplitude."); pw.println(" If -a is provided, the waveform will use the advanced APIs to "); pw.println(" If -a is provided, the waveform will use the advanced APIs to "); pw.println(" generate the vibration pattern and the input parameters "); pw.println(" generate the vibration pattern and the input parameters "); pw.println(" become [<duration1> <amplitude1> <frequency1>]."); pw.println(" become [<duration1> <amplitude1> <frequency1>]."); Loading @@ -3132,19 +3147,20 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { pw.println(" If -r is provided, the waveform loops back to the specified index"); pw.println(" If -r is provided, the waveform loops back to the specified index"); pw.println(" (e.g. 0 loops from the beginning)."); pw.println(" (e.g. 0 loops from the beginning)."); pw.println(" prebaked [-w delay] [-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(" Vibrates with prebaked effect."); 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(" If -w is provided, the effect will be played after the specified"); pw.println(" wait time in milliseconds."); pw.println(" wait time in milliseconds."); pw.println(" If -b is provided, the prebaked fallback effect will be played if"); 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(" the device doesn't support the given effect-id."); pw.println(" primitives ([-w delay] <primitive-id>)..."); pw.print(" primitives ([-w delay] [-o time] [-s scale]"); pw.println(" Vibrates with a composed effect; ignored when device is on DND "); pw.println("<primitive-id> [<scale>])..."); pw.println(" (Do Not Disturb) mode; touch feedback strength user setting "); pw.println(" Vibrates with a composed effect."); pw.println(" will be used to scale primitive intensities."); pw.println(" If -w is provided, the next primitive will be played after the "); pw.println(" If -w is provided, the next primitive will be played after the "); pw.println(" specified wait time in milliseconds."); pw.println(" specified wait time in milliseconds."); pw.println(" If -o is provided, the next primitive will be played at the "); pw.println(" specified start offset time in milliseconds."); pw.println(" If -s is provided, the next primitive will be played with the"); pw.println(" specified amplitude scale, in a scale of [0,1]."); pw.println(""); pw.println(""); pw.println("Common Options:"); pw.println("Common Options:"); pw.println(" -f"); pw.println(" -f"); Loading @@ -3155,6 +3171,11 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { pw.println(" -d <description>"); pw.println(" -d <description>"); pw.println(" Add description to the vibration."); pw.println(" Add description to the vibration."); pw.println(""); pw.println(""); pw.println("Notes"); pw.println(" Vibrations triggered by these commands will be ignored when"); pw.println(" device is on DND (Do Not Disturb) mode; notification strength"); pw.println(" user settings will be applied for scale."); pw.println(""); } } } } } } Loading Loading
services/core/java/com/android/server/vibrator/VibratorManagerService.java +89 −68 Original line number Original line Diff line number Diff line Loading @@ -61,6 +61,7 @@ import android.os.VibratorInfo; import android.os.vibrator.Flags; import android.os.vibrator.Flags; import android.os.vibrator.IVibrationSessionCallback; import android.os.vibrator.IVibrationSessionCallback; import android.os.vibrator.PrebakedSegment; import android.os.vibrator.PrebakedSegment; import android.os.vibrator.PrimitiveSegment; import android.os.vibrator.VibrationConfig; import android.os.vibrator.VibrationConfig; import android.os.vibrator.VibrationEffectSegment; import android.os.vibrator.VibrationEffectSegment; import android.os.vibrator.VibratorInfoFactory; import android.os.vibrator.VibratorInfoFactory; Loading Loading @@ -2672,7 +2673,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { CombinedVibration.ParallelCombination combination = CombinedVibration.ParallelCombination combination = CombinedVibration.startParallel(); CombinedVibration.startParallel(); while ("-v".equals(getNextOption())) { while ("-v".equals(getNextOption())) { int vibratorId = Integer.parseInt(getNextArgRequired()); int vibratorId = parseInt(getNextArgRequired(), "Expected vibrator id after -v"); combination.addVibrator(vibratorId, nextEffect()); combination.addVibrator(vibratorId, nextEffect()); } } runVibrate(commonOptions, combination.combine()); runVibrate(commonOptions, combination.combine()); Loading @@ -2684,7 +2685,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { CombinedVibration.SequentialCombination combination = CombinedVibration.SequentialCombination combination = CombinedVibration.startSequential(); CombinedVibration.startSequential(); while ("-v".equals(getNextOption())) { while ("-v".equals(getNextOption())) { int vibratorId = Integer.parseInt(getNextArgRequired()); int vibratorId = parseInt(getNextArgRequired(), "Expected vibrator id after -v"); combination.addNext(vibratorId, nextEffect()); combination.addNext(vibratorId, nextEffect()); } } runVibrate(commonOptions, combination.combine()); runVibrate(commonOptions, combination.combine()); Loading @@ -2709,7 +2710,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { private int runHapticFeedback() { private int runHapticFeedback() { CommonOptions commonOptions = new CommonOptions(); CommonOptions commonOptions = new CommonOptions(); int constant = Integer.parseInt(getNextArgRequired()); int constant = parseInt(getNextArgRequired(), "Expected haptic feedback constant id"); IBinder deathBinder = commonOptions.background ? VibratorManagerService.this IBinder deathBinder = commonOptions.background ? VibratorManagerService.this : mShellCallbacksToken; : mShellCallbacksToken; Loading Loading @@ -2757,12 +2758,13 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { if ("-a".equals(nextOption)) { if ("-a".equals(nextOption)) { hasAmplitude = true; hasAmplitude = true; } else if ("-w".equals(nextOption)) { } else if ("-w".equals(nextOption)) { delay = Integer.parseInt(getNextArgRequired()); delay = parseInt(getNextArgRequired(), "Expected delay millis after -w"); } } } } long duration = Long.parseLong(getNextArgRequired()); long duration = parseInt(getNextArgRequired(), "Expected one-shot duration millis"); int amplitude = hasAmplitude ? Integer.parseInt(getNextArgRequired()) int amplitude = hasAmplitude ? parseInt(getNextArgRequired(), "Expected one-shot amplitude") : VibrationEffect.DEFAULT_AMPLITUDE; : VibrationEffect.DEFAULT_AMPLITUDE; composition.addOffDuration(Duration.ofMillis(delay)); composition.addOffDuration(Duration.ofMillis(delay)); composition.addEffect(VibrationEffect.createOneShot(duration, amplitude)); composition.addEffect(VibrationEffect.createOneShot(duration, amplitude)); Loading Loading @@ -2837,8 +2839,10 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { while ((nextOption = getNextOption()) != null) { while ((nextOption = getNextOption()) != null) { switch (nextOption) { switch (nextOption) { case "-a" -> isAdvanced = true; case "-a" -> isAdvanced = true; case "-i" -> initialSharpness = Float.parseFloat(getNextArgRequired()); case "-i" -> initialSharpness = parseFloat(getNextArgRequired(), case "-r" -> repeat = Integer.parseInt(getNextArgRequired()); "Expected initial sharpness after -i"); case "-r" -> repeat = parseInt(getNextArgRequired(), "Expected repeat index after -r"); } } } } Loading @@ -2864,8 +2868,8 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { // nextArg is not a duration, finish reading. // nextArg is not a duration, finish reading. break; break; } } intensity = Float.parseFloat(getNextArgRequired()); intensity = parseFloat(getNextArgRequired(), "Expected envelope intensity"); sharpness = Float.parseFloat(getNextArgRequired()); sharpness = parseFloat(getNextArgRequired(), "Expected envelope sharpness"); builder.addControlPoint(intensity, sharpness, duration); builder.addControlPoint(intensity, sharpness, duration); pos++; pos++; } } Loading Loading @@ -2893,16 +2897,14 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { getNextArgRequired(); // consume "waveform" getNextArgRequired(); // consume "waveform" String nextOption; String nextOption; while ((nextOption = getNextOption()) != null) { while ((nextOption = getNextOption()) != null) { if ("-a".equals(nextOption)) { switch (nextOption) { hasAmplitudes = true; case "-a" -> hasAmplitudes = true; } else if ("-r".equals(nextOption)) { case "-f" -> hasFrequencies = true; repeat = Integer.parseInt(getNextArgRequired()); case "-c" -> isContinuous = true; } else if ("-w".equals(nextOption)) { case "-r" -> repeat = parseInt(getNextArgRequired(), delay = Integer.parseInt(getNextArgRequired()); "Expected repeat index after -r"); } else if ("-f".equals(nextOption)) { case "-w" -> delay = parseInt(getNextArgRequired(), hasFrequencies = true; "Expected delay millis after -w"); } else if ("-c".equals(nextOption)) { isContinuous = true; } } } } List<Integer> durations = new ArrayList<>(); List<Integer> durations = new ArrayList<>(); Loading @@ -2920,14 +2922,15 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { break; break; } } if (hasAmplitudes) { if (hasAmplitudes) { amplitudes.add( int amplitude = parseInt(getNextArgRequired(), "Expected waveform amplitude"); Float.parseFloat(getNextArgRequired()) / VibrationEffect.MAX_AMPLITUDE); amplitudes.add((float) amplitude / VibrationEffect.MAX_AMPLITUDE); } else { } else { amplitudes.add(nextAmplitude); amplitudes.add(nextAmplitude); nextAmplitude = 1 - nextAmplitude; nextAmplitude = 1 - nextAmplitude; } } if (hasFrequencies) { if (hasFrequencies) { frequencies.add(Float.parseFloat(getNextArgRequired())); frequencies.add( parseFloat(getNextArgRequired(), "Expected waveform frequency")); } } } } Loading Loading @@ -2986,27 +2989,37 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { if ("-b".equals(nextOption)) { if ("-b".equals(nextOption)) { shouldFallback = true; shouldFallback = true; } else if ("-w".equals(nextOption)) { } else if ("-w".equals(nextOption)) { delay = Integer.parseInt(getNextArgRequired()); delay = parseInt(getNextArgRequired(), "Expected delay millis after -w"); } } } } int effectId = Integer.parseInt(getNextArgRequired()); int effectId = parseInt(getNextArgRequired(), "Expected prebaked effect id"); composition.addOffDuration(Duration.ofMillis(delay)); composition.addOffDuration(Duration.ofMillis(delay)); composition.addEffect(VibrationEffect.get(effectId, shouldFallback)); composition.addEffect(VibrationEffect.get(effectId, shouldFallback)); } } private void addPrimitivesToComposition(VibrationEffect.Composition composition) { private void addPrimitivesToComposition(VibrationEffect.Composition composition) { getNextArgRequired(); // consume "primitives" getNextArgRequired(); // consume "primitives" String nextArg; while (peekNextArg() != null) { while ((nextArg = peekNextArg()) != null) { int delay = 0; int delay = 0; if ("-w".equals(nextArg)) { float scale = 1f; getNextArgRequired(); // consume "-w" int delayType = PrimitiveSegment.DEFAULT_DELAY_TYPE; delay = Integer.parseInt(getNextArgRequired()); nextArg = peekNextArg(); String nextOption; while ((nextOption = getNextOption()) != null) { if ("-s".equals(nextOption)) { scale = parseFloat(getNextArgRequired(), "Expected scale after -s"); } else if ("-o".equals(nextOption)) { delayType = VibrationEffect.Composition.DELAY_TYPE_RELATIVE_START_OFFSET; delay = parseInt(getNextArgRequired(), "Expected offset millis after -o"); } else if ("-w".equals(nextOption)) { delayType = PrimitiveSegment.DEFAULT_DELAY_TYPE; delay = parseInt(getNextArgRequired(), "Expected delay millis after -w"); } } } try { try { composition.addPrimitive(Integer.parseInt(nextArg), /* scale= */ 1, delay); String nextArg = peekNextArg(); // Just in case this is not a primitive. composition.addPrimitive(Integer.parseInt(nextArg), scale, delay, delayType); getNextArgRequired(); // consume the primitive id getNextArgRequired(); // consume the primitive id } catch (NumberFormatException | NullPointerException e) { } catch (NumberFormatException | NullPointerException e) { // nextArg is not describing a primitive, leave it to be consumed by outer loops // nextArg is not describing a primitive, leave it to be consumed by outer loops Loading @@ -3032,17 +3045,15 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { VibrationXmlParser.parseDocument(new StringReader(xml)); VibrationXmlParser.parseDocument(new StringReader(xml)); VibratorInfo combinedVibratorInfo = getCombinedVibratorInfo(); VibratorInfo combinedVibratorInfo = getCombinedVibratorInfo(); if (combinedVibratorInfo == null) { if (combinedVibratorInfo == null) { throw new IllegalStateException( throw new IllegalStateException("No vibrator info available to parse XML"); "No combined vibrator info to parse vibration XML " + xml); } } VibrationEffect effect = parsedVibration.resolve(combinedVibratorInfo); VibrationEffect effect = parsedVibration.resolve(combinedVibratorInfo); if (effect == null) { if (effect == null) { throw new IllegalArgumentException( throw new IllegalArgumentException("Parsed XML cannot be resolved: " + xml); "Parsed vibration cannot be resolved for vibration XML " + xml); } } return CombinedVibration.createParallel(effect); return CombinedVibration.createParallel(effect); } catch (IOException e) { } catch (IOException e) { throw new RuntimeException("Error parsing vibration XML " + xml, e); throw new RuntimeException("Error parsing XML: " + xml, e); } } } } Loading @@ -3060,16 +3071,30 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { } } } } private static int parseInt(String text, String errorMessage) { try { return Integer.parseInt(text); } catch (NumberFormatException | NullPointerException e) { throw new IllegalArgumentException(errorMessage, e); } } private static float parseFloat(String text, String errorMessage) { try { return Float.parseFloat(text); } catch (NumberFormatException | NullPointerException e) { throw new IllegalArgumentException(errorMessage, e); } } @Override @Override public void onHelp() { public void onHelp() { try (PrintWriter pw = getOutPrintWriter();) { try (PrintWriter pw = getOutPrintWriter();) { pw.println("Vibrator Manager commands:"); pw.println("Vibrator Manager commands:"); pw.println(" help"); pw.println(" help"); pw.println(" Prints this help text."); pw.println(" Prints this help text."); pw.println(""); pw.println(" list"); pw.println(" list"); pw.println(" Prints the id of device vibrators. This does not include any "); pw.println(" Prints device vibrator ids; does not include input devices."); 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(" Vibrates effect on all vibrators in sync."); pw.println(" combined [options] (-v <vibrator-id> <effect>...)..."); pw.println(" combined [options] (-v <vibrator-id> <effect>...)..."); Loading @@ -3079,51 +3104,41 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { pw.println(" xml [options] <xml>"); pw.println(" xml [options] <xml>"); pw.println(" Vibrates using combined vibration described in given XML string"); pw.println(" Vibrates using combined vibration described in given XML string"); pw.println(" on all vibrators in sync. The XML could be:"); pw.println(" on all vibrators in sync. The XML could be:"); pw.println(" XML containing a single effect, or"); pw.println(" A single <vibration-effect>, or"); pw.println(" A vibration select XML containing multiple effects."); pw.println(" A <vibration-select> containing multiple effects."); pw.println(" Vibrates using combined vibration described in given XML string."); pw.println(" feedback [options] <constant>"); pw.println(" XML containing a single effect it runs on all vibrators in sync."); pw.println(" Performs a haptic feedback with the given constant."); pw.println(" cancel"); pw.println(" cancel"); pw.println(" Cancels any active vibration"); pw.println(" Cancels any active vibration"); pw.println(" feedback [-f] [-d <description>] <constant>"); pw.println(" Performs a haptic feedback with the given constant."); pw.println(" The force (-f) option enables the `always` configuration, which"); pw.println(" plays the haptic irrespective of the vibration intensity settings"); pw.println(""); pw.println(""); pw.println("Effect commands:"); pw.println("Effect commands:"); pw.println(" oneshot [-w delay] [-a] <duration> [<amplitude>]"); pw.println(" oneshot [-w delay] [-a] <duration> [<amplitude>]"); pw.println(" Vibrates for duration milliseconds; ignored when device is on "); pw.println(" Vibrates for duration milliseconds."); 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(" If -w is provided, the effect will be played after the specified"); pw.println(" wait time in milliseconds."); pw.println(" wait time in milliseconds."); pw.println(" If -a is provided, the command accepts a second argument for "); pw.println(" If -a is provided, the command accepts a second argument for "); pw.println(" amplitude, in a scale of 1-255."); pw.println(" amplitude, in a scale of 1-255."); pw.print(" waveform [-w delay] [-r index] [-a] [-f] [-c] "); pw.print(" waveform [-w delay] [-r index] [-a] [-f] [-c] "); pw.println("(<duration> [<amplitude>] [<frequency>])..."); pw.println("(<duration> [<amplitude>] [<frequency>])..."); pw.println(" Vibrates for durations and amplitudes in list; ignored when "); pw.println(" Vibrates for durations and amplitudes in list."); 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(" If -w is provided, the effect will be played after the specified"); pw.println(" wait time in milliseconds."); pw.println(" wait time in milliseconds."); pw.println(" If -r is provided, the waveform loops back to the specified"); 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(" index (e.g. 0 loops from the beginning)."); pw.println(" If -a is provided, the command expects amplitude to follow each"); pw.println(" If -a is provided, the command expects amplitude to follow each"); pw.println(" duration; otherwise, it accepts durations only and alternates"); pw.println(" duration; otherwise, it accepts durations only and alternates"); pw.println(" off/on"); pw.println(" off/on."); pw.println(" If -f is provided, the command expects frequency to follow each"); pw.println(" If -f is provided, the command expects frequency to follow each"); pw.println(" amplitude or duration; otherwise, it uses resonant frequency"); pw.println(" amplitude or duration; otherwise, it uses resonant frequency."); pw.println(" If -c is provided, the waveform is continuous and will ramp"); pw.println(" If -c is provided, the waveform is continuous and will ramp"); pw.println(" between values; otherwise each entry is a fixed step."); pw.println(" between values; otherwise each entry is a fixed step."); pw.println(" Duration is in milliseconds; amplitude is a scale of 1-255;"); pw.println(" Duration is in milliseconds; amplitude is a scale of 1-255;"); pw.println(" frequency is an absolute value in hertz;"); pw.println(" frequency is an absolute value in hertz."); pw.print(" envelope [-a] [-i initial sharpness] [-r index] "); pw.print(" envelope [-a] [-i initial sharpness] [-r index] "); pw.println("[<duration1> <intensity1> <sharpness1>]..."); pw.println("[<duration1> <intensity1> <sharpness1>]..."); pw.println(" Generates a vibration pattern based on a series of duration, "); pw.println(" Generates a vibration pattern based on a series of duration, "); pw.println(" intensity, and sharpness values. The total vibration time is "); pw.println(" intensity, and sharpness values. The total vibration time is "); pw.println(" the sum of all durations; Ignored when device is on "); pw.println(" the sum of all durations."); pw.println(" DND (Do Not Disturb) mode; touch feedback strength user setting "); pw.println(" will be used to scale amplitude."); pw.println(" If -a is provided, the waveform will use the advanced APIs to "); pw.println(" If -a is provided, the waveform will use the advanced APIs to "); pw.println(" generate the vibration pattern and the input parameters "); pw.println(" generate the vibration pattern and the input parameters "); pw.println(" become [<duration1> <amplitude1> <frequency1>]."); pw.println(" become [<duration1> <amplitude1> <frequency1>]."); Loading @@ -3132,19 +3147,20 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { pw.println(" If -r is provided, the waveform loops back to the specified index"); pw.println(" If -r is provided, the waveform loops back to the specified index"); pw.println(" (e.g. 0 loops from the beginning)."); pw.println(" (e.g. 0 loops from the beginning)."); pw.println(" prebaked [-w delay] [-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(" Vibrates with prebaked effect."); 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(" If -w is provided, the effect will be played after the specified"); pw.println(" wait time in milliseconds."); pw.println(" wait time in milliseconds."); pw.println(" If -b is provided, the prebaked fallback effect will be played if"); 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(" the device doesn't support the given effect-id."); pw.println(" primitives ([-w delay] <primitive-id>)..."); pw.print(" primitives ([-w delay] [-o time] [-s scale]"); pw.println(" Vibrates with a composed effect; ignored when device is on DND "); pw.println("<primitive-id> [<scale>])..."); pw.println(" (Do Not Disturb) mode; touch feedback strength user setting "); pw.println(" Vibrates with a composed effect."); pw.println(" will be used to scale primitive intensities."); pw.println(" If -w is provided, the next primitive will be played after the "); pw.println(" If -w is provided, the next primitive will be played after the "); pw.println(" specified wait time in milliseconds."); pw.println(" specified wait time in milliseconds."); pw.println(" If -o is provided, the next primitive will be played at the "); pw.println(" specified start offset time in milliseconds."); pw.println(" If -s is provided, the next primitive will be played with the"); pw.println(" specified amplitude scale, in a scale of [0,1]."); pw.println(""); pw.println(""); pw.println("Common Options:"); pw.println("Common Options:"); pw.println(" -f"); pw.println(" -f"); Loading @@ -3155,6 +3171,11 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { pw.println(" -d <description>"); pw.println(" -d <description>"); pw.println(" Add description to the vibration."); pw.println(" Add description to the vibration."); pw.println(""); pw.println(""); pw.println("Notes"); pw.println(" Vibrations triggered by these commands will be ignored when"); pw.println(" device is on DND (Do Not Disturb) mode; notification strength"); pw.println(" user settings will be applied for scale."); pw.println(""); } } } } } } Loading