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

Commit b5219782 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Introduce vibrator service effect pipeline support" into main

parents 25fbada7 9bc0f1eb
Loading
Loading
Loading
Loading
+59 −3
Original line number Original line Diff line number Diff line
@@ -17,6 +17,7 @@
package android.os;
package android.os;


import android.annotation.NonNull;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
import android.annotation.TestApi;
import android.os.vibrator.Flags;
import android.os.vibrator.Flags;
import android.util.SparseArray;
import android.util.SparseArray;
@@ -28,6 +29,7 @@ import java.util.List;
import java.util.Locale;
import java.util.Locale;
import java.util.Objects;
import java.util.Objects;
import java.util.StringJoiner;
import java.util.StringJoiner;
import java.util.function.Function;


/**
/**
 * A CombinedVibration describes a combination of haptic effects to be performed by one or more
 * A CombinedVibration describes a combination of haptic effects to be performed by one or more
@@ -113,6 +115,17 @@ public abstract class CombinedVibration implements Parcelable {
    @TestApi
    @TestApi
    public abstract long getDuration();
    public abstract long getDuration();


    /**
     * Gets the estimated duration of the combined vibration in milliseconds.
     *
     * <p>For effects with hardware-dependent constants (e.g. primitive compositions), this returns
     * the estimated duration based on the {@link VibratorInfo}. For all other effects this will
     * return the same as {@link #getDuration()}.
     *
     * @hide
     */
    public abstract long getDuration(@Nullable SparseArray<VibratorInfo> vibratorInfos);

    /**
    /**
     * Returns true if this effect could represent a touch haptic feedback.
     * Returns true if this effect could represent a touch haptic feedback.
     *
     *
@@ -381,6 +394,23 @@ public abstract class CombinedVibration implements Parcelable {
            return mEffect.getDuration();
            return mEffect.getDuration();
        }
        }


        /** @hide */
        @Override
        public long getDuration(@Nullable SparseArray<VibratorInfo> vibratorInfos) {
            if (vibratorInfos == null) {
                return getDuration();
            }
            long maxDuration = 0;
            for (int i = 0; i < vibratorInfos.size(); i++) {
                long duration = mEffect.getDuration(vibratorInfos.valueAt(i));
                if ((duration == Long.MAX_VALUE) || (duration < 0)) {
                    return duration;
                }
                maxDuration = Math.max(maxDuration, duration);
            }
            return maxDuration;
        }

        /** @hide */
        /** @hide */
        @Override
        @Override
        public boolean isHapticFeedbackCandidate() {
        public boolean isHapticFeedbackCandidate() {
@@ -531,10 +561,27 @@ public abstract class CombinedVibration implements Parcelable {


        @Override
        @Override
        public long getDuration() {
        public long getDuration() {
            return getDuration(idx -> mEffects.valueAt(idx).getDuration());
        }

        /** @hide */
        @Override
        public long getDuration(@Nullable SparseArray<VibratorInfo> vibratorInfos) {
            if (vibratorInfos == null) {
                return getDuration();
            }
            return getDuration(idx -> {
                VibrationEffect effect = mEffects.valueAt(idx);
                VibratorInfo info = vibratorInfos.get(mEffects.keyAt(idx));
                return effect.getDuration(info);
            });
        }

        private long getDuration(Function<Integer, Long> durationFn) {
            long maxDuration = Long.MIN_VALUE;
            long maxDuration = Long.MIN_VALUE;
            boolean hasUnknownStep = false;
            boolean hasUnknownStep = false;
            for (int i = 0; i < mEffects.size(); i++) {
            for (int i = 0; i < mEffects.size(); i++) {
                long duration = mEffects.valueAt(i).getDuration();
                long duration = durationFn.apply(i);
                if (duration == Long.MAX_VALUE) {
                if (duration == Long.MAX_VALUE) {
                    // If any duration is repeating, this combination duration is also repeating.
                    // If any duration is repeating, this combination duration is also repeating.
                    return duration;
                    return duration;
@@ -750,12 +797,21 @@ public abstract class CombinedVibration implements Parcelable {


        @Override
        @Override
        public long getDuration() {
        public long getDuration() {
            return getDuration(CombinedVibration::getDuration);
        }

        /** @hide */
        @Override
        public long getDuration(@Nullable SparseArray<VibratorInfo> vibratorInfos) {
            return getDuration(effect -> effect.getDuration(vibratorInfos));
        }

        private long getDuration(Function<CombinedVibration, Long> durationFn) {
            boolean hasUnknownStep = false;
            boolean hasUnknownStep = false;
            long durations = 0;
            long durations = 0;
            final int effectCount = mEffects.size();
            final int effectCount = mEffects.size();
            for (int i = 0; i < effectCount; i++) {
            for (int i = 0; i < effectCount; i++) {
                CombinedVibration effect = mEffects.get(i);
                long duration = durationFn.apply(mEffects.get(i));
                long duration = effect.getDuration();
                if (duration == Long.MAX_VALUE) {
                if (duration == Long.MAX_VALUE) {
                    // If any duration is repeating, this combination duration is also repeating.
                    // If any duration is repeating, this combination duration is also repeating.
                    return duration;
                    return duration;
+25 −1
Original line number Original line Diff line number Diff line
@@ -55,6 +55,7 @@ import java.util.Locale;
import java.util.Objects;
import java.util.Objects;
import java.util.StringJoiner;
import java.util.StringJoiner;
import java.util.function.BiFunction;
import java.util.function.BiFunction;
import java.util.function.Function;


/**
/**
 * A VibrationEffect describes a haptic effect to be performed by a {@link Vibrator}.
 * A VibrationEffect describes a haptic effect to be performed by a {@link Vibrator}.
@@ -564,6 +565,19 @@ public abstract class VibrationEffect implements Parcelable {
    @TestApi
    @TestApi
    public abstract long getDuration();
    public abstract long getDuration();


    /**
     * Gets the estimated duration of the segment for given vibrator, in milliseconds.
     *
     * <p>For effects with hardware-dependent constants (e.g. primitive compositions), this returns
     * the estimated duration based on the given {@link VibratorInfo}. For all other effects this
     * will return the same as {@link #getDuration()}.
     *
     * @hide
     */
    public long getDuration(@Nullable VibratorInfo vibratorInfo) {
        return getDuration();
    }

    /**
    /**
     * Checks if a vibrator with a given {@link VibratorInfo} can play this effect as intended.
     * Checks if a vibrator with a given {@link VibratorInfo} can play this effect as intended.
     *
     *
@@ -904,13 +918,23 @@ public abstract class VibrationEffect implements Parcelable {


        @Override
        @Override
        public long getDuration() {
        public long getDuration() {
            return getDuration(VibrationEffectSegment::getDuration);
        }

        /** @hide */
        @Override
        public long getDuration(@Nullable VibratorInfo vibratorInfo) {
            return getDuration(segment -> segment.getDuration(vibratorInfo));
        }

        private long getDuration(Function<VibrationEffectSegment, Long> durationFn) {
            if (mRepeatIndex >= 0) {
            if (mRepeatIndex >= 0) {
                return Long.MAX_VALUE;
                return Long.MAX_VALUE;
            }
            }
            int segmentCount = mSegments.size();
            int segmentCount = mSegments.size();
            long totalDuration = 0;
            long totalDuration = 0;
            for (int i = 0; i < segmentCount; i++) {
            for (int i = 0; i < segmentCount; i++) {
                long segmentDuration = mSegments.get(i).getDuration();
                long segmentDuration = durationFn.apply(mSegments.get(i));
                if (segmentDuration < 0) {
                if (segmentDuration < 0) {
                    return segmentDuration;
                    return segmentDuration;
                }
                }
+69 −38
Original line number Original line Diff line number Diff line
@@ -16,6 +16,17 @@


package android.os.vibrator;
package android.os.vibrator;


import static android.os.VibrationEffect.Composition.PRIMITIVE_CLICK;
import static android.os.VibrationEffect.Composition.PRIMITIVE_THUD;
import static android.os.VibrationEffect.Composition.PRIMITIVE_TICK;
import static android.os.VibrationEffect.EFFECT_CLICK;
import static android.os.VibrationEffect.EFFECT_DOUBLE_CLICK;
import static android.os.VibrationEffect.EFFECT_HEAVY_CLICK;
import static android.os.VibrationEffect.EFFECT_POP;
import static android.os.VibrationEffect.EFFECT_TEXTURE_TICK;
import static android.os.VibrationEffect.EFFECT_THUD;
import static android.os.VibrationEffect.EFFECT_TICK;

import android.annotation.NonNull;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Nullable;
import android.annotation.TestApi;
import android.annotation.TestApi;
@@ -76,6 +87,32 @@ public final class PrebakedSegment extends VibrationEffectSegment {
        return -1;
        return -1;
    }
    }


    /** @hide */
    @Override
    public long getDuration(@Nullable VibratorInfo vibratorInfo) {
        if (vibratorInfo == null) {
            return getDuration();
        }
        return switch (mEffectId) {
            case EFFECT_TICK,
                 EFFECT_CLICK,
                 EFFECT_HEAVY_CLICK -> estimateFromPrimitiveDuration(vibratorInfo, PRIMITIVE_CLICK);
            case EFFECT_TEXTURE_TICK -> estimateFromPrimitiveDuration(vibratorInfo, PRIMITIVE_TICK);
            case EFFECT_THUD -> estimateFromPrimitiveDuration(vibratorInfo, PRIMITIVE_THUD);
            case EFFECT_DOUBLE_CLICK -> {
                long clickDuration = vibratorInfo.getPrimitiveDuration(PRIMITIVE_CLICK);
                yield clickDuration > 0 ? 2 * clickDuration : getDuration();
            }
            default -> getDuration();
        };
    }

    private long estimateFromPrimitiveDuration(VibratorInfo vibratorInfo, int primitiveId) {
        int duration = vibratorInfo.getPrimitiveDuration(primitiveId);
        // Unsupported primitives should be ignored here.
        return duration > 0 ? duration : getDuration();
    }

    /** @hide */
    /** @hide */
    @Override
    @Override
    public boolean areVibrationFeaturesSupported(@NonNull VibratorInfo vibratorInfo) {
    public boolean areVibrationFeaturesSupported(@NonNull VibratorInfo vibratorInfo) {
@@ -89,34 +126,30 @@ public final class PrebakedSegment extends VibrationEffectSegment {
        }
        }
        // The vibrator does not have hardware support for the effect, but the effect has fallback
        // The vibrator does not have hardware support for the effect, but the effect has fallback
        // support. Check if a fallback will be available for the effect ID.
        // support. Check if a fallback will be available for the effect ID.
        switch (mEffectId) {
        return switch (mEffectId) {
            case VibrationEffect.EFFECT_CLICK:
            case VibrationEffect.EFFECT_DOUBLE_CLICK:
            case VibrationEffect.EFFECT_HEAVY_CLICK:
            case VibrationEffect.EFFECT_TICK:
            // Any of these effects are always supported via some form of fallback.
            // Any of these effects are always supported via some form of fallback.
                return true;
            case EFFECT_CLICK,
            default:
                 EFFECT_DOUBLE_CLICK,
                return false;
                 EFFECT_HEAVY_CLICK,
        }
                 EFFECT_TICK -> true;
            default -> false;
        };
    }
    }


    /** @hide */
    /** @hide */
    @Override
    @Override
    public boolean isHapticFeedbackCandidate() {
    public boolean isHapticFeedbackCandidate() {
        switch (mEffectId) {
        return switch (mEffectId) {
            case VibrationEffect.EFFECT_CLICK:
            case EFFECT_CLICK,
            case VibrationEffect.EFFECT_DOUBLE_CLICK:
                 EFFECT_DOUBLE_CLICK,
            case VibrationEffect.EFFECT_HEAVY_CLICK:
                 EFFECT_HEAVY_CLICK,
            case VibrationEffect.EFFECT_POP:
                 EFFECT_POP,
            case VibrationEffect.EFFECT_TEXTURE_TICK:
                 EFFECT_TEXTURE_TICK,
            case VibrationEffect.EFFECT_THUD:
                 EFFECT_THUD,
            case VibrationEffect.EFFECT_TICK:
                 EFFECT_TICK -> true;
                return true;
            default:
            // VibrationEffect.RINGTONES are not segments that could represent a haptic feedback
            // VibrationEffect.RINGTONES are not segments that could represent a haptic feedback
                return false;
            default -> false;
        }
        };
    }
    }


    /** @hide */
    /** @hide */
@@ -153,27 +186,25 @@ public final class PrebakedSegment extends VibrationEffectSegment {
    }
    }


    private static boolean isValidEffectStrength(int strength) {
    private static boolean isValidEffectStrength(int strength) {
        switch (strength) {
        return switch (strength) {
            case VibrationEffect.EFFECT_STRENGTH_LIGHT:
            case VibrationEffect.EFFECT_STRENGTH_LIGHT,
            case VibrationEffect.EFFECT_STRENGTH_MEDIUM:
                 VibrationEffect.EFFECT_STRENGTH_MEDIUM,
            case VibrationEffect.EFFECT_STRENGTH_STRONG:
                 VibrationEffect.EFFECT_STRENGTH_STRONG -> true;
                return true;
            default -> false;
            default:
        };
                return false;
        }
    }
    }


    /** @hide */
    /** @hide */
    @Override
    @Override
    public void validate() {
    public void validate() {
        switch (mEffectId) {
        switch (mEffectId) {
            case VibrationEffect.EFFECT_CLICK:
            case EFFECT_CLICK:
            case VibrationEffect.EFFECT_DOUBLE_CLICK:
            case EFFECT_DOUBLE_CLICK:
            case VibrationEffect.EFFECT_HEAVY_CLICK:
            case EFFECT_HEAVY_CLICK:
            case VibrationEffect.EFFECT_POP:
            case EFFECT_POP:
            case VibrationEffect.EFFECT_TEXTURE_TICK:
            case EFFECT_TEXTURE_TICK:
            case VibrationEffect.EFFECT_THUD:
            case EFFECT_THUD:
            case VibrationEffect.EFFECT_TICK:
            case EFFECT_TICK:
                break;
                break;
            default:
            default:
                int[] ringtones = VibrationEffect.RINGTONES;
                int[] ringtones = VibrationEffect.RINGTONES;
+10 −0
Original line number Original line Diff line number Diff line
@@ -75,6 +75,16 @@ public final class PrimitiveSegment extends VibrationEffectSegment {
        return -1;
        return -1;
    }
    }


    /** @hide */
    @Override
    public long getDuration(@Nullable VibratorInfo vibratorInfo) {
        if (vibratorInfo == null) {
            return getDuration();
        }
        int duration = vibratorInfo.getPrimitiveDuration(mPrimitiveId);
        return duration > 0 ? duration + mDelay : getDuration();
    }

    /** @hide */
    /** @hide */
    @Override
    @Override
    public boolean areVibrationFeaturesSupported(@NonNull VibratorInfo vibratorInfo) {
    public boolean areVibrationFeaturesSupported(@NonNull VibratorInfo vibratorInfo) {
+20 −0
Original line number Original line Diff line number Diff line
@@ -86,6 +86,7 @@ public class VibrationConfig {
    private final int mDefaultKeyboardVibrationIntensity;
    private final int mDefaultKeyboardVibrationIntensity;


    private final boolean mKeyboardVibrationSettingsSupported;
    private final boolean mKeyboardVibrationSettingsSupported;
    private final int mVibrationPipelineMaxDurationMs;


    /** @hide */
    /** @hide */
    public VibrationConfig(@Nullable Resources resources) {
    public VibrationConfig(@Nullable Resources resources) {
@@ -106,6 +107,8 @@ public class VibrationConfig {
                com.android.internal.R.bool.config_ignoreVibrationsOnWirelessCharger);
                com.android.internal.R.bool.config_ignoreVibrationsOnWirelessCharger);
        mKeyboardVibrationSettingsSupported = loadBoolean(resources,
        mKeyboardVibrationSettingsSupported = loadBoolean(resources,
                com.android.internal.R.bool.config_keyboardVibrationSettingsSupported);
                com.android.internal.R.bool.config_keyboardVibrationSettingsSupported);
        mVibrationPipelineMaxDurationMs = loadInteger(resources,
                com.android.internal.R.integer.config_vibrationPipelineMaxDuration, 0);


        mDefaultAlarmVibrationIntensity = loadDefaultIntensity(resources,
        mDefaultAlarmVibrationIntensity = loadDefaultIntensity(resources,
                com.android.internal.R.integer.config_defaultAlarmVibrationIntensity);
                com.android.internal.R.integer.config_defaultAlarmVibrationIntensity);
@@ -220,6 +223,23 @@ public class VibrationConfig {
        return mRampStepDurationMs;
        return mRampStepDurationMs;
    }
    }


    /**
     * The max duration, in milliseconds, allowed for pipelining vibration requests.
     *
     * <p>If the ongoing vibration duration is shorter than this threshold then it should be allowed
     * to finish before the next vibration can start. If the ongoing vibration is longer than this
     * then it should be cancelled when it's superseded for the new one.
     *
     * @return the max duration allowed for vibration effect to finish before the next request, or
     * zero to disable effect pipelining.
     */
    public int getVibrationPipelineMaxDurationMs() {
        if (mVibrationPipelineMaxDurationMs < 0) {
            return 0;
        }
        return mVibrationPipelineMaxDurationMs;
    }

    /**
    /**
     * Whether or not vibrations are ignored if the device is on a wireless charger.
     * Whether or not vibrations are ignored if the device is on a wireless charger.
     *
     *
Loading