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

Commit 0cd0aaae authored by Michael Wright's avatar Michael Wright Committed by Automerger Merge Worker
Browse files

Merge "Play any composition in VibrationThread" into sc-dev am: eadeea40

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/13929332

Change-Id: I8d85404e555fdd729e99b386fdaf7f64c012974e
parents 5533a9e8 eadeea40
Loading
Loading
Loading
Loading
+395 −216

File changed.

Preview size limit exceeded, changes collapsed.

+16 −8
Original line number Diff line number Diff line
@@ -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;
        }
    }

    /**
@@ -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) {
@@ -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)) {
@@ -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);
@@ -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. */
+100 −68
Original line number Diff line number Diff line
@@ -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;
                    }
                }
@@ -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;
        }
@@ -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,
@@ -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,
@@ -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) {
@@ -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.");
+8 −7
Original line number Diff line number Diff line
@@ -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) {
@@ -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,
@@ -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) {
@@ -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},
+2 −1
Original line number Diff line number Diff line
@@ -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