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

Commit 8ecfc26c authored by Lais Andrade's avatar Lais Andrade
Browse files

Fix VibrationThread handling of IVibrator errors

The vibration playback is ignoring the HAL result for vibrate requests,
causing the vibration to continue even after one of the steps
is unsupported or failed.

This also causes the vibration request to be reported as successful in
the vibrator service dumpsys, which makes debugging difficult when the
user reports no vibration was actually performed by the device.

This fix introduces a new status code for HAL errors and updates the
VibrationThread to stop the vibration steps once a step reports
unsupported or failed status.

Bug: 419572960
Flag: android.os.vibrator.vibration_thread_handling_hal_failure
Test: VibrationThreadTest
Change-Id: Ib6a8ea6041185212d07204b2ee24e0d592c51083
parent b7e63c3f
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -136,3 +136,13 @@ flag {
      purpose: PURPOSE_FEATURE
    }
}

flag {
    namespace: "haptics"
    name: "vibration_thread_handling_hal_failure"
    description: "Fixes how VibrationThread handled HAL failures"
    bug: "419572960"
    metadata {
        purpose: PURPOSE_BUGFIX
    }
}
+2 −1
Original line number Diff line number Diff line
@@ -145,9 +145,10 @@ message VibrationProto {
        IGNORED_FROM_VIRTUAL_DEVICE = 26;
        IGNORED_ON_WIRELESS_CHARGER = 27;
        IGNORED_MISSING_PERMISSION = 28;
        IGNORED_INVALID_REQUEST = 31;
        CANCELLED_BY_APP_OPS = 29;
        CANCELLED_BY_FOREGROUND_USER = 30;
        IGNORED_INVALID_REQUEST = 31;
        IGNORED_ERROR_DISPATCHING = 32;
        reserved 17; // prev IGNORED_UNKNOWN_VIBRATION
    }
}
+41 −1
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.server.vibrator;

import android.os.SystemClock;
import android.os.VibrationEffect;
import android.os.vibrator.Flags;

import java.util.List;

@@ -48,12 +49,45 @@ abstract class AbstractComposedVibratorStep extends AbstractVibratorStep {
        this.segmentIndex = index;
    }

    /**
     * Return the {@link VibrationStepConductor#nextVibrateStep} to start right away, skipping the
     * current segment from the effect.
     */
    protected List<Step> skipStep() {
        return Flags.vibrationThreadHandlingHalFailure()
                ? skipStep(SystemClock.uptimeMillis())
                // Preserve old behavior when fix is not enabled.
                : vibratorOnNextSteps(/* segmentsPlayed= */ 1);
    }

    /**
     * Return the {@link VibrationStepConductor#nextVibrateStep} to start at given time, skipping
     * the current segment from the effect.
     */
    protected List<Step> skipStep(long nextStartTime) {
        return nextSteps(nextStartTime, /* segmentsPlayed= */ 1);
    }

    /**
     * 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.
     *
     * <p>This should be used when the vibrator result is responsible for the step execution timing,
     * and it will cancel the playback if the HAL result is unsupported or failure.
     */
    protected List<Step> nextSteps(int segmentsPlayed) {
    protected List<Step> vibratorOnNextSteps(int segmentsPlayed) {
        if (Flags.vibrationThreadHandlingHalFailure()) {
            if (mVibratorOnResult > 0) {
                // Vibrator was turned on by this step, with mVibratorOnResult as the duration.
                // Schedule next steps for right after the vibration finishes.
                long nextStartTime = SystemClock.uptimeMillis() + mVibratorOnResult;
                return nextSteps(nextStartTime, segmentsPlayed);
            } else {
                // Step unsupported or failed, cancel the vibration on this vibrator.
                return cancelStep();
            }
        }
        // Schedule next steps to run right away.
        long nextStartTime = SystemClock.uptimeMillis();
        if (mVibratorOnResult > 0) {
@@ -86,4 +120,10 @@ abstract class AbstractComposedVibratorStep extends AbstractVibratorStep {
                nextSegmentIndex, mPendingVibratorOffDeadline);
        return List.of(nextStep);
    }

    /** Return next steps for cancelling the vibration playback. */
    protected List<Step> cancelStep() {
        return List.of(new CompleteEffectVibratorStep(conductor, SystemClock.uptimeMillis(),
                /* cancelled= */ true, controller, /* pendingVibratorOffDeadline= */ 0));
    }
}
+2 −5
Original line number Diff line number Diff line
@@ -62,8 +62,7 @@ final class ComposePrimitivesVibratorStep extends AbstractComposedVibratorStep {
            if (primitives.isEmpty()) {
                Slog.w(VibrationThread.TAG, "Ignoring wrong segment for a ComposePrimitivesStep: "
                        + effect.getSegments().get(segmentIndex));
                // Skip this step and play the next one right away.
                return nextSteps(/* segmentsPlayed= */ 1);
                return skipStep();
            }

            if (VibrationThread.DEBUG) {
@@ -77,9 +76,7 @@ final class ComposePrimitivesVibratorStep extends AbstractComposedVibratorStep {
            long vibratorOnResult = controller.on(primitivesArray, getVibration().id, stepId);
            handleVibratorOnResult(vibratorOnResult);
            getVibration().stats.reportComposePrimitives(vibratorOnResult, primitivesArray);

            // The next start and off times will be calculated from mVibratorOnResult.
            return nextSteps(/* segmentsPlayed= */ primitives.size());
            return vibratorOnNextSteps(/* segmentsPlayed= */ primitives.size());
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
        }
+3 −7
Original line number Diff line number Diff line
@@ -49,8 +49,7 @@ final class ComposePwleV2VibratorStep extends AbstractComposedVibratorStep {
    @Override
    public List<Step> play() {
        if (!Flags.normalizedPwleEffects()) {
            // Skip this step and play the next one right away.
            return nextSteps(/* segmentsPlayed= */ 1);
            return skipStep();
        }

        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "ComposePwleV2Step");
@@ -63,8 +62,7 @@ final class ComposePwleV2VibratorStep extends AbstractComposedVibratorStep {
            if (pwles.isEmpty()) {
                Slog.w(VibrationThread.TAG, "Ignoring wrong segment for a ComposeEnvelopeStep: "
                        + effect.getSegments().get(segmentIndex));
                // Skip this step and play the next one right away.
                return nextSteps(/* segmentsPlayed= */ 1);
                return skipStep();
            }

            if (VibrationThread.DEBUG) {
@@ -76,9 +74,7 @@ final class ComposePwleV2VibratorStep extends AbstractComposedVibratorStep {
            long vibratorOnResult = controller.on(pwlesArray, getVibration().id, stepId);
            handleVibratorOnResult(vibratorOnResult);
            getVibration().stats.reportComposePwle(vibratorOnResult, pwlesArray);

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