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

Commit da7e89fd authored by Lais Andrade's avatar Lais Andrade Committed by Android (Google) Code Review
Browse files

Merge "Fix composed VibrationEffect timing out"

parents cb7ea3c9 0cc2d21e
Loading
Loading
Loading
Loading
+56 −36
Original line number Diff line number Diff line
@@ -31,9 +31,9 @@ abstract class AbstractVibratorStep extends Step {
    public final VibratorController controller;
    public final VibrationEffect.Composed effect;
    public final int segmentIndex;
    public final long previousStepVibratorOffTimeout;

    long mVibratorOnResult;
    long mPendingVibratorOffDeadline;
    boolean mVibratorCompleteCallbackReceived;

    /**
@@ -43,19 +43,19 @@ abstract class AbstractVibratorStep extends Step {
     * @param controller         The vibrator that is playing the effect.
     * @param effect             The effect being played in this step.
     * @param index              The index of the next segment to be played by this step
     * @param previousStepVibratorOffTimeout The time the vibrator is expected to complete any
     * @param pendingVibratorOffDeadline The time the vibrator is expected to complete any
     *                           previous vibration and turn off. This is used to allow this step to
     *                           be triggered when the completion callback is received, and can
     *                           be used to play effects back-to-back.
     */
    AbstractVibratorStep(VibrationStepConductor conductor, long startTime,
            VibratorController controller, VibrationEffect.Composed effect, int index,
            long previousStepVibratorOffTimeout) {
            long pendingVibratorOffDeadline) {
        super(conductor, startTime);
        this.controller = controller;
        this.effect = effect;
        this.segmentIndex = index;
        this.previousStepVibratorOffTimeout = previousStepVibratorOffTimeout;
        mPendingVibratorOffDeadline = pendingVibratorOffDeadline;
    }

    public int getVibratorId() {
@@ -69,27 +69,57 @@ abstract class AbstractVibratorStep extends Step {

    @Override
    public boolean acceptVibratorCompleteCallback(int vibratorId) {
        boolean isSameVibrator = controller.getVibratorInfo().getId() == vibratorId;
        mVibratorCompleteCallbackReceived |= isSameVibrator;
        if (getVibratorId() != vibratorId) {
            return false;
        }

        // Only activate this step if a timeout was set to wait for the vibration to complete,
        // otherwise we are waiting for the correct time to play the next step.
        return isSameVibrator && (previousStepVibratorOffTimeout > SystemClock.uptimeMillis());
        boolean shouldAcceptCallback = mPendingVibratorOffDeadline > SystemClock.uptimeMillis();
        if (VibrationThread.DEBUG) {
            Slog.d(VibrationThread.TAG,
                    "Received completion callback from " + vibratorId
                            + ", accepted = " + shouldAcceptCallback);
        }

        // The callback indicates this vibrator has stopped, reset the timeout.
        mPendingVibratorOffDeadline = 0;
        mVibratorCompleteCallbackReceived = true;
        return shouldAcceptCallback;
    }

    @Override
    public List<Step> cancel() {
        return Arrays.asList(new CompleteEffectVibratorStep(conductor, SystemClock.uptimeMillis(),
                /* cancelled= */ true, controller, previousStepVibratorOffTimeout));
                /* cancelled= */ true, controller, mPendingVibratorOffDeadline));
    }

    @Override
    public void cancelImmediately() {
        if (previousStepVibratorOffTimeout > SystemClock.uptimeMillis()) {
        if (mPendingVibratorOffDeadline > SystemClock.uptimeMillis()) {
            // Vibrator might be running from previous steps, so turn it off while canceling.
            stopVibrating();
        }
    }

    protected long handleVibratorOnResult(long vibratorOnResult) {
        mVibratorOnResult = vibratorOnResult;
        if (VibrationThread.DEBUG) {
            Slog.d(VibrationThread.TAG,
                    "Turned on vibrator " + getVibratorId() + ", result = " + mVibratorOnResult);
        }
        if (mVibratorOnResult > 0) {
            // Vibrator was turned on by this step, with vibratorOnResult as the duration.
            // Set an extra timeout to wait for the vibrator completion callback.
            mPendingVibratorOffDeadline = SystemClock.uptimeMillis() + mVibratorOnResult
                    + VibrationStepConductor.CALLBACKS_EXTRA_TIMEOUT;
        } else {
            // Vibrator does not support the request or failed to turn on, reset callback deadline.
            mPendingVibratorOffDeadline = 0;
        }
        return mVibratorOnResult;
    }

    protected void stopVibrating() {
        if (VibrationThread.DEBUG) {
            Slog.d(VibrationThread.TAG,
@@ -97,6 +127,7 @@ abstract class AbstractVibratorStep extends Step {
        }
        controller.off();
        getVibration().stats().reportVibratorOff();
        mPendingVibratorOffDeadline = 0;
    }

    protected void changeAmplitude(float amplitude) {
@@ -109,40 +140,29 @@ abstract class AbstractVibratorStep extends Step {
    }

    /**
     * Return the {@link VibrationStepConductor#nextVibrateStep} with same timings, only jumping
     * the segments.
     */
    protected List<Step> skipToNextSteps(int segmentsSkipped) {
        return nextSteps(startTime, previousStepVibratorOffTimeout, segmentsSkipped);
    }

    /**
     * Return the {@link VibrationStepConductor#nextVibrateStep} with same start and off timings
     * calculated from {@link #getVibratorOnDuration()}, jumping all played segments.
     *
     * <p>This method has same behavior as {@link #skipToNextSteps(int)} when the vibrator
     * result is non-positive, meaning the vibrator has either ignored or failed to turn on.
     * Return the {@link VibrationStepConductor#nextVibrateStep} with start and off timings
     * calculated from {@link #getVibratorOnDuration()} based on the current
     * {@link SystemClock#uptimeMillis()} and jumping all played segments from the effect.
     */
    protected List<Step> nextSteps(int segmentsPlayed) {
        if (mVibratorOnResult <= 0) {
            // Vibration was not started, so just skip the played segments and keep timings.
            return skipToNextSteps(segmentsPlayed);
        // Schedule next steps to run right away.
        long nextStartTime = SystemClock.uptimeMillis();
        if (mVibratorOnResult > 0) {
            // Vibrator was turned on by this step, with mVibratorOnResult as the duration.
            // Schedule next steps for right after the vibration finishes.
            nextStartTime += mVibratorOnResult;
        }
        long nextStartTime = SystemClock.uptimeMillis() + mVibratorOnResult;
        long nextVibratorOffTimeout =
                nextStartTime + VibrationStepConductor.CALLBACKS_EXTRA_TIMEOUT;
        return nextSteps(nextStartTime, nextVibratorOffTimeout, segmentsPlayed);
        return nextSteps(nextStartTime, segmentsPlayed);
    }

    /**
     * Return the {@link VibrationStepConductor#nextVibrateStep} with given start and off timings,
     * which might be calculated independently, jumping all played segments.
     * Return the {@link VibrationStepConductor#nextVibrateStep} with given start time,
     * which might be calculated independently, and jumping all played segments from the effect.
     *
     * <p>This should be used when the vibrator on/off state is not responsible for the steps
     * execution timings, e.g. while playing the vibrator amplitudes.
     * <p>This should be used when the vibrator on/off state is not responsible for the step
     * execution timing, e.g. while playing the vibrator amplitudes.
     */
    protected List<Step> nextSteps(long nextStartTime, long vibratorOffTimeout,
            int segmentsPlayed) {
    protected List<Step> nextSteps(long nextStartTime, int segmentsPlayed) {
        int nextSegmentIndex = segmentIndex + segmentsPlayed;
        int effectSize = effect.getSegments().size();
        int repeatIndex = effect.getRepeatIndex();
@@ -154,7 +174,7 @@ abstract class AbstractVibratorStep extends Step {
            nextSegmentIndex = repeatIndex + ((nextSegmentIndex - effectSize) % loopSize);
        }
        Step nextStep = conductor.nextVibrateStep(nextStartTime, controller, effect,
                nextSegmentIndex, vibratorOffTimeout);
                nextSegmentIndex, mPendingVibratorOffDeadline);
        return nextStep == null ? VibrationStepConductor.EMPTY_STEP_LIST : Arrays.asList(nextStep);
    }
}
+16 −8
Original line number Diff line number Diff line
@@ -34,9 +34,9 @@ final class CompleteEffectVibratorStep extends AbstractVibratorStep {
    private final boolean mCancelled;

    CompleteEffectVibratorStep(VibrationStepConductor conductor, long startTime, boolean cancelled,
            VibratorController controller, long previousStepVibratorOffTimeout) {
            VibratorController controller, long pendingVibratorOffDeadline) {
        super(conductor, startTime, controller, /* effect= */ null, /* index= */ -1,
                previousStepVibratorOffTimeout);
                pendingVibratorOffDeadline);
        mCancelled = cancelled;
    }

@@ -73,10 +73,11 @@ final class CompleteEffectVibratorStep extends AbstractVibratorStep {
                return VibrationStepConductor.EMPTY_STEP_LIST;
            }

            long now = SystemClock.uptimeMillis();
            float currentAmplitude = controller.getCurrentAmplitude();
            long remainingOnDuration =
                    previousStepVibratorOffTimeout - VibrationStepConductor.CALLBACKS_EXTRA_TIMEOUT
                            - SystemClock.uptimeMillis();
                    mPendingVibratorOffDeadline - now
                            - VibrationStepConductor.CALLBACKS_EXTRA_TIMEOUT;
            long rampDownDuration =
                    Math.min(remainingOnDuration,
                            conductor.vibrationSettings.getRampDownDuration());
@@ -89,8 +90,10 @@ final class CompleteEffectVibratorStep extends AbstractVibratorStep {
                    stopVibrating();
                    return VibrationStepConductor.EMPTY_STEP_LIST;
                } else {
                    // Vibration is completing normally, turn off after the deadline in case we
                    // don't receive the callback in time (callback also triggers it right away).
                    return Arrays.asList(new TurnOffVibratorStep(
                            conductor, previousStepVibratorOffTimeout, controller));
                            conductor, mPendingVibratorOffDeadline, controller));
                }
            }

@@ -100,13 +103,18 @@ final class CompleteEffectVibratorStep extends AbstractVibratorStep {
                                + " from amplitude " + currentAmplitude
                                + " for " + rampDownDuration + "ms");
            }

            // If we are cancelling this vibration then make sure the vibrator will be turned off
            // immediately after the ramp off duration. Otherwise, this is a planned ramp off for
            // the remaining ON duration, then just propagate the mPendingVibratorOffDeadline so the
            // turn off step will wait for the vibration completion callback and end gracefully.
            long rampOffVibratorOffDeadline =
                    mCancelled ? (now + rampDownDuration) : mPendingVibratorOffDeadline;
            float amplitudeDelta = currentAmplitude / (rampDownDuration / stepDownDuration);
            float amplitudeTarget = currentAmplitude - amplitudeDelta;
            long newVibratorOffTimeout =
                    mCancelled ? rampDownDuration : previousStepVibratorOffTimeout;
            return Arrays.asList(
                    new RampOffVibratorStep(conductor, startTime, amplitudeTarget, amplitudeDelta,
                            controller, newVibratorOffTimeout));
                            controller, rampOffVibratorOffDeadline));
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
        }
+11 −7
Original line number Diff line number Diff line
@@ -40,11 +40,11 @@ final class ComposePrimitivesVibratorStep extends AbstractVibratorStep {

    ComposePrimitivesVibratorStep(VibrationStepConductor conductor, long startTime,
            VibratorController controller, VibrationEffect.Composed effect, int index,
            long previousStepVibratorOffTimeout) {
            long pendingVibratorOffDeadline) {
        // This step should wait for the last vibration to finish (with the timeout) and for the
        // intended step start time (to respect the effect delays).
        super(conductor, Math.max(startTime, previousStepVibratorOffTimeout), controller, effect,
                index, previousStepVibratorOffTimeout);
        super(conductor, Math.max(startTime, pendingVibratorOffDeadline), controller, effect,
                index, pendingVibratorOffDeadline);
    }

    @Override
@@ -60,18 +60,22 @@ final class ComposePrimitivesVibratorStep extends AbstractVibratorStep {
            if (primitives.isEmpty()) {
                Slog.w(VibrationThread.TAG, "Ignoring wrong segment for a ComposePrimitivesStep: "
                        + effect.getSegments().get(segmentIndex));
                return skipToNextSteps(/* segmentsSkipped= */ 1);
                // Skip this step and play the next one right away.
                return nextSteps(/* segmentsPlayed= */ 1);
            }

            if (VibrationThread.DEBUG) {
                Slog.d(VibrationThread.TAG, "Compose " + primitives + " primitives on vibrator "
                        + controller.getVibratorInfo().getId());
                        + getVibratorId());
            }

            PrimitiveSegment[] primitivesArray =
                    primitives.toArray(new PrimitiveSegment[primitives.size()]);
            mVibratorOnResult = controller.on(primitivesArray, getVibration().id);
            getVibration().stats().reportComposePrimitives(mVibratorOnResult, primitivesArray);
            long vibratorOnResult = controller.on(primitivesArray, getVibration().id);
            handleVibratorOnResult(vibratorOnResult);
            getVibration().stats().reportComposePrimitives(vibratorOnResult, primitivesArray);

            // The next start and off times will be calculated from mVibratorOnResult.
            return nextSteps(/* segmentsPlayed= */ primitives.size());
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+9 −6
Original line number Diff line number Diff line
@@ -41,11 +41,11 @@ final class ComposePwleVibratorStep extends AbstractVibratorStep {

    ComposePwleVibratorStep(VibrationStepConductor conductor, long startTime,
            VibratorController controller, VibrationEffect.Composed effect, int index,
            long previousStepVibratorOffTimeout) {
            long pendingVibratorOffDeadline) {
        // This step should wait for the last vibration to finish (with the timeout) and for the
        // intended step start time (to respect the effect delays).
        super(conductor, Math.max(startTime, previousStepVibratorOffTimeout), controller, effect,
                index, previousStepVibratorOffTimeout);
        super(conductor, Math.max(startTime, pendingVibratorOffDeadline), controller, effect,
                index, pendingVibratorOffDeadline);
    }

    @Override
@@ -61,7 +61,8 @@ final class ComposePwleVibratorStep extends AbstractVibratorStep {
            if (pwles.isEmpty()) {
                Slog.w(VibrationThread.TAG, "Ignoring wrong segment for a ComposePwleStep: "
                        + effect.getSegments().get(segmentIndex));
                return skipToNextSteps(/* segmentsSkipped= */ 1);
                // Skip this step and play the next one right away.
                return nextSteps(/* segmentsPlayed= */ 1);
            }

            if (VibrationThread.DEBUG) {
@@ -69,9 +70,11 @@ final class ComposePwleVibratorStep extends AbstractVibratorStep {
                        + controller.getVibratorInfo().getId());
            }
            RampSegment[] pwlesArray = pwles.toArray(new RampSegment[pwles.size()]);
            mVibratorOnResult = controller.on(pwlesArray, getVibration().id);
            getVibration().stats().reportComposePwle(mVibratorOnResult, pwlesArray);
            long vibratorOnResult = controller.on(pwlesArray, getVibration().id);
            handleVibratorOnResult(vibratorOnResult);
            getVibration().stats().reportComposePwle(vibratorOnResult, pwlesArray);

            // The next start and off times will be calculated from mVibratorOnResult.
            return nextSteps(/* segmentsPlayed= */ pwles.size());
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+12 −9
Original line number Diff line number Diff line
@@ -35,11 +35,11 @@ final class PerformPrebakedVibratorStep extends AbstractVibratorStep {

    PerformPrebakedVibratorStep(VibrationStepConductor conductor, long startTime,
            VibratorController controller, VibrationEffect.Composed effect, int index,
            long previousStepVibratorOffTimeout) {
            long pendingVibratorOffDeadline) {
        // This step should wait for the last vibration to finish (with the timeout) and for the
        // intended step start time (to respect the effect delays).
        super(conductor, Math.max(startTime, previousStepVibratorOffTimeout), controller, effect,
                index, previousStepVibratorOffTimeout);
        super(conductor, Math.max(startTime, pendingVibratorOffDeadline), controller, effect,
                index, pendingVibratorOffDeadline);
    }

    @Override
@@ -50,7 +50,8 @@ final class PerformPrebakedVibratorStep extends AbstractVibratorStep {
            if (!(segment instanceof PrebakedSegment)) {
                Slog.w(VibrationThread.TAG, "Ignoring wrong segment for a "
                        + "PerformPrebakedVibratorStep: " + segment);
                return skipToNextSteps(/* segmentsSkipped= */ 1);
                // Skip this step and play the next one right away.
                return nextSteps(/* segmentsPlayed= */ 1);
            }

            PrebakedSegment prebaked = (PrebakedSegment) segment;
@@ -61,10 +62,11 @@ final class PerformPrebakedVibratorStep extends AbstractVibratorStep {
            }

            VibrationEffect fallback = getVibration().getFallback(prebaked.getEffectId());
            mVibratorOnResult = controller.on(prebaked, getVibration().id);
            getVibration().stats().reportPerformEffect(mVibratorOnResult, prebaked);
            long vibratorOnResult = controller.on(prebaked, getVibration().id);
            handleVibratorOnResult(vibratorOnResult);
            getVibration().stats().reportPerformEffect(vibratorOnResult, prebaked);

            if (mVibratorOnResult == 0 && prebaked.shouldFallback()
            if (vibratorOnResult == 0 && prebaked.shouldFallback()
                    && (fallback instanceof VibrationEffect.Composed)) {
                if (VibrationThread.DEBUG) {
                    Slog.d(VibrationThread.TAG, "Playing fallback for effect "
@@ -72,14 +74,15 @@ final class PerformPrebakedVibratorStep extends AbstractVibratorStep {
                }
                AbstractVibratorStep fallbackStep = conductor.nextVibrateStep(startTime, controller,
                        replaceCurrentSegment((VibrationEffect.Composed) fallback),
                        segmentIndex, previousStepVibratorOffTimeout);
                        segmentIndex, mPendingVibratorOffDeadline);
                List<Step> fallbackResult = fallbackStep.play();
                // Update the result with the fallback result so this step is seamlessly
                // replaced by the fallback to any outer application of this.
                mVibratorOnResult = fallbackStep.getVibratorOnDuration();
                handleVibratorOnResult(fallbackStep.getVibratorOnDuration());
                return fallbackResult;
            }

            // The next start and off times will be calculated from mVibratorOnResult.
            return nextSteps(/* segmentsPlayed= */ 1);
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
Loading