Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6944e462 authored by Lais Andrade's avatar Lais Andrade
Browse files

Introduce primitive scale to vibrator adb

Introduce support for primitive composition scale and start-offset
parameters to vibrator manager command line client.

Fix: 294813629
Test: N/A
Flag: EXEMPT introducing adb commands to existing APIs
Change-Id: I19dc98859972602225716283554c9908b52d5fa2
parent 703ee5ae
Loading
Loading
Loading
Loading
+89 −68
Original line number Diff line number Diff line
@@ -61,6 +61,7 @@ import android.os.VibratorInfo;
import android.os.vibrator.Flags;
import android.os.vibrator.IVibrationSessionCallback;
import android.os.vibrator.PrebakedSegment;
import android.os.vibrator.PrimitiveSegment;
import android.os.vibrator.VibrationConfig;
import android.os.vibrator.VibrationEffectSegment;
import android.os.vibrator.VibratorInfoFactory;
@@ -2672,7 +2673,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
            CombinedVibration.ParallelCombination combination =
                    CombinedVibration.startParallel();
            while ("-v".equals(getNextOption())) {
                int vibratorId = Integer.parseInt(getNextArgRequired());
                int vibratorId = parseInt(getNextArgRequired(), "Expected vibrator id after -v");
                combination.addVibrator(vibratorId, nextEffect());
            }
            runVibrate(commonOptions, combination.combine());
@@ -2684,7 +2685,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
            CombinedVibration.SequentialCombination combination =
                    CombinedVibration.startSequential();
            while ("-v".equals(getNextOption())) {
                int vibratorId = Integer.parseInt(getNextArgRequired());
                int vibratorId = parseInt(getNextArgRequired(), "Expected vibrator id after -v");
                combination.addNext(vibratorId, nextEffect());
            }
            runVibrate(commonOptions, combination.combine());
@@ -2709,7 +2710,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {

        private int runHapticFeedback() {
            CommonOptions commonOptions = new CommonOptions();
            int constant = Integer.parseInt(getNextArgRequired());
            int constant = parseInt(getNextArgRequired(), "Expected haptic feedback constant id");

            IBinder deathBinder = commonOptions.background ? VibratorManagerService.this
                    : mShellCallbacksToken;
@@ -2757,12 +2758,13 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
                if ("-a".equals(nextOption)) {
                    hasAmplitude = true;
                } else if ("-w".equals(nextOption)) {
                    delay = Integer.parseInt(getNextArgRequired());
                    delay = parseInt(getNextArgRequired(), "Expected delay millis after -w");
                }
            }

            long duration = Long.parseLong(getNextArgRequired());
            int amplitude = hasAmplitude ? Integer.parseInt(getNextArgRequired())
            long duration = parseInt(getNextArgRequired(), "Expected one-shot duration millis");
            int amplitude = hasAmplitude
                    ? parseInt(getNextArgRequired(), "Expected one-shot amplitude")
                    : VibrationEffect.DEFAULT_AMPLITUDE;
            composition.addOffDuration(Duration.ofMillis(delay));
            composition.addEffect(VibrationEffect.createOneShot(duration, amplitude));
@@ -2837,8 +2839,10 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
            while ((nextOption = getNextOption()) != null) {
                switch (nextOption) {
                    case "-a" -> isAdvanced = true;
                    case "-i" -> initialSharpness = Float.parseFloat(getNextArgRequired());
                    case "-r" -> repeat = Integer.parseInt(getNextArgRequired());
                    case "-i" -> initialSharpness = parseFloat(getNextArgRequired(),
                            "Expected initial sharpness after -i");
                    case "-r" -> repeat = parseInt(getNextArgRequired(),
                            "Expected repeat index after -r");
                }
            }

@@ -2864,8 +2868,8 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
                    // nextArg is not a duration, finish reading.
                    break;
                }
                intensity = Float.parseFloat(getNextArgRequired());
                sharpness = Float.parseFloat(getNextArgRequired());
                intensity = parseFloat(getNextArgRequired(), "Expected envelope intensity");
                sharpness = parseFloat(getNextArgRequired(), "Expected envelope sharpness");
                builder.addControlPoint(intensity, sharpness, duration);
                pos++;
            }
@@ -2893,16 +2897,14 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
            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());
                } else if ("-f".equals(nextOption)) {
                    hasFrequencies = true;
                } else if ("-c".equals(nextOption)) {
                    isContinuous = true;
                switch (nextOption) {
                    case "-a" -> hasAmplitudes = true;
                    case "-f" -> hasFrequencies = true;
                    case "-c" -> isContinuous = true;
                    case "-r" -> repeat = parseInt(getNextArgRequired(),
                            "Expected repeat index after -r");
                    case "-w" -> delay = parseInt(getNextArgRequired(),
                            "Expected delay millis after -w");
                }
            }
            List<Integer> durations = new ArrayList<>();
@@ -2920,14 +2922,15 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
                    break;
                }
                if (hasAmplitudes) {
                    amplitudes.add(
                            Float.parseFloat(getNextArgRequired()) / VibrationEffect.MAX_AMPLITUDE);
                    int amplitude = parseInt(getNextArgRequired(), "Expected waveform amplitude");
                    amplitudes.add((float) amplitude / VibrationEffect.MAX_AMPLITUDE);
                } else {
                    amplitudes.add(nextAmplitude);
                    nextAmplitude = 1 - nextAmplitude;
                }
                if (hasFrequencies) {
                    frequencies.add(Float.parseFloat(getNextArgRequired()));
                    frequencies.add(
                            parseFloat(getNextArgRequired(), "Expected waveform frequency"));
                }
            }

@@ -2986,27 +2989,37 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
                if ("-b".equals(nextOption)) {
                    shouldFallback = true;
                } 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.addEffect(VibrationEffect.get(effectId, shouldFallback));
        }

        private void addPrimitivesToComposition(VibrationEffect.Composition composition) {
            getNextArgRequired(); // consume "primitives"
            String nextArg;
            while ((nextArg = peekNextArg()) != null) {
            while (peekNextArg() != null) {
                int delay = 0;
                if ("-w".equals(nextArg)) {
                    getNextArgRequired(); // consume "-w"
                    delay = Integer.parseInt(getNextArgRequired());
                    nextArg = peekNextArg();
                float scale = 1f;
                int delayType = PrimitiveSegment.DEFAULT_DELAY_TYPE;

                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 {
                    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
                } catch (NumberFormatException | NullPointerException e) {
                    // nextArg is not describing a primitive, leave it to be consumed by outer loops
@@ -3032,17 +3045,15 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
                        VibrationXmlParser.parseDocument(new StringReader(xml));
                VibratorInfo combinedVibratorInfo = getCombinedVibratorInfo();
                if (combinedVibratorInfo == null) {
                    throw new IllegalStateException(
                            "No combined vibrator info to parse vibration XML " + xml);
                    throw new IllegalStateException("No vibrator info available to parse XML");
                }
                VibrationEffect effect = parsedVibration.resolve(combinedVibratorInfo);
                if (effect == null) {
                    throw new IllegalArgumentException(
                            "Parsed vibration cannot be resolved for vibration XML " + xml);
                    throw new IllegalArgumentException("Parsed XML cannot be resolved: " + xml);
                }
                return CombinedVibration.createParallel(effect);
            } catch (IOException e) {
                throw new RuntimeException("Error parsing vibration XML " + xml, e);
                throw new RuntimeException("Error parsing XML: " + xml, e);
            }
        }

@@ -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
        public void onHelp() {
            try (PrintWriter pw = getOutPrintWriter();) {
                pw.println("Vibrator Manager commands:");
                pw.println("  help");
                pw.println("    Prints this help text.");
                pw.println("");
                pw.println("  list");
                pw.println("    Prints the id of device vibrators. This does not include any ");
                pw.println("    connected input device.");
                pw.println("    Prints device vibrator ids; does not include input devices.");
                pw.println("  synced [options] <effect>...");
                pw.println("    Vibrates effect on all vibrators in sync.");
                pw.println("  combined [options] (-v <vibrator-id> <effect>...)...");
@@ -3079,51 +3104,41 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
                pw.println("  xml [options] <xml>");
                pw.println("    Vibrates using combined vibration described in given XML string");
                pw.println("    on all vibrators in sync. The XML could be:");
                pw.println("        XML containing a single effect, or");
                pw.println("        A vibration select XML containing multiple effects.");
                pw.println("    Vibrates using combined vibration described in given XML string.");
                pw.println("    XML containing a single effect it runs on all vibrators in sync.");
                pw.println("        A single <vibration-effect>, or");
                pw.println("        A <vibration-select> containing multiple effects.");
                pw.println("  feedback [options] <constant>");
                pw.println("    Performs a haptic feedback with the given constant.");
                pw.println("  cancel");
                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("Effect commands:");
                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("    Vibrates for duration milliseconds.");
                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.print("  waveform [-w delay] [-r index] [-a] [-f] [-c] ");
                pw.println("(<duration> [<amplitude>] [<frequency>])...");
                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("    Vibrates for durations and amplitudes in list.");
                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("    index (e.g. 0 loops from the beginning).");
                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("    off/on");
                pw.println("    off/on.");
                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("    between values; otherwise each entry is a fixed step.");
                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.println("[<duration1> <intensity1> <sharpness1>]...");
                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("    the sum of all durations; 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("    the sum of all durations.");
                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("    become [<duration1> <amplitude1> <frequency1>].");
@@ -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("    (e.g. 0 loops from the beginning).");
                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("    Vibrates with prebaked effect.");
                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("  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.");
                pw.print("  primitives ([-w delay] [-o time] [-s scale]");
                pw.println("<primitive-id> [<scale>])...");
                pw.println("    Vibrates with a composed effect.");
                pw.println("    If -w is provided, the next primitive will be played after the ");
                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("Common Options:");
                pw.println("  -f");
@@ -3155,6 +3171,11 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
                pw.println("  -d <description>");
                pw.println("    Add description to the vibration.");
                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("");
            }
        }
    }