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

Commit d6fae9a1 authored by Lais Andrade's avatar Lais Andrade
Browse files

Create single thread to play any Vibration

This thread takes in a CombinedVibrationEffect and plays all vibrations.

The Prebaked effect now also holds a fallback VibrationEffect, which is
resolved and scaled together with the original effect and is passed on
to the VibrateThread. All class attributes are not final, making this
effect immutable as all the others.

The Vibration now takes in a CombinedVibrationEffect, in preparation to
be used by the VibratorManagerService in multiple vibrators.

The new thread is replacing all vibration from VibratorService, which
means they are all triggering the HAL asynchronously.

Bug: 167946816
Bug: 131311651
Test: VibrationThreadTest, VibratorServiceTest, VibrationEffectTest, VibrationScalerTest
Change-Id: Ic27b35e63ca35ad47083f94da9ca7bd75b683d43
parent 13658939
Loading
Loading
Loading
Loading
+1 −2
Original line number Diff line number Diff line
@@ -1329,11 +1329,10 @@ package android.os {

  public static class VibrationEffect.Prebaked extends android.os.VibrationEffect implements android.os.Parcelable {
    ctor public VibrationEffect.Prebaked(android.os.Parcel);
    ctor public VibrationEffect.Prebaked(int, boolean);
    ctor public VibrationEffect.Prebaked(int, boolean, int);
    method public long getDuration();
    method public int getEffectStrength();
    method public int getId();
    method public void setEffectStrength(int);
    method public boolean shouldFallback();
    method public void writeToParcel(android.os.Parcel, int);
    field @NonNull public static final android.os.Parcelable.Creator<android.os.VibrationEffect.Prebaked> CREATOR;
+67 −4
Original line number Diff line number Diff line
@@ -86,9 +86,15 @@ public abstract class CombinedVibrationEffect implements Parcelable {
        return 0;
    }

    /** @hide */
    public abstract long getDuration();

    /** @hide */
    public abstract void validate();

    /** @hide */
    public abstract boolean hasVibrator(int vibratorId);

    /**
     * A combination of haptic effects that should be played in multiple vibrators in sync.
     *
@@ -265,19 +271,29 @@ public abstract class CombinedVibrationEffect implements Parcelable {
            return mEffect;
        }

        @Override
        public long getDuration() {
            return mEffect.getDuration();
        }

        /** @hide */
        @Override
        public void validate() {
            mEffect.validate();
        }

        @Override
        public boolean hasVibrator(int vibratorId) {
            return true;
        }

        @Override
        public boolean equals(Object o) {
            if (!(o instanceof Mono)) {
                return false;
            }
            Mono other = (Mono) o;
            return other.mEffect.equals(other.mEffect);
            return mEffect.equals(other.mEffect);
        }

        @Override
@@ -345,6 +361,15 @@ public abstract class CombinedVibrationEffect implements Parcelable {
            return mEffects;
        }

        @Override
        public long getDuration() {
            long maxDuration = Long.MIN_VALUE;
            for (int i = 0; i < mEffects.size(); i++) {
                maxDuration = Math.max(maxDuration, mEffects.valueAt(i).getDuration());
            }
            return maxDuration;
        }

        /** @hide */
        @Override
        public void validate() {
@@ -355,6 +380,11 @@ public abstract class CombinedVibrationEffect implements Parcelable {
            }
        }

        @Override
        public boolean hasVibrator(int vibratorId) {
            return mEffects.indexOfKey(vibratorId) >= 0;
        }

        @Override
        public boolean equals(Object o) {
            if (!(o instanceof Stereo)) {
@@ -445,6 +475,26 @@ public abstract class CombinedVibrationEffect implements Parcelable {
            return mDelays;
        }

        @Override
        public long getDuration() {
            long durations = 0;
            final int effectCount = mEffects.size();
            for (int i = 0; i < effectCount; i++) {
                CombinedVibrationEffect effect = mEffects.get(i);
                long duration = effect.getDuration();
                if (duration < 0) {
                    // If any duration is unknown, this combination duration is also unknown.
                    return duration;
                }
                durations += duration;
            }
            long delays = 0;
            for (int i = 0; i < effectCount; i++) {
                delays += mDelays.get(i);
            }
            return durations + delays;
        }

        /** @hide */
        @Override
        public void validate() {
@@ -452,13 +502,15 @@ public abstract class CombinedVibrationEffect implements Parcelable {
                    "There should be at least one effect set for a combined effect");
            Preconditions.checkArgument(mEffects.size() == mDelays.size(),
                    "Effect and delays should have equal length");
            for (long delay : mDelays) {
                if (delay < 0) {
            final int effectCount = mEffects.size();
            for (int i = 0; i < effectCount; i++) {
                if (mDelays.get(i) < 0) {
                    throw new IllegalArgumentException("Delays must all be >= 0"
                            + " (delays=" + mDelays + ")");
                }
            }
            for (CombinedVibrationEffect effect : mEffects) {
            for (int i = 0; i < effectCount; i++) {
                CombinedVibrationEffect effect = mEffects.get(i);
                if (effect instanceof Sequential) {
                    throw new IllegalArgumentException(
                            "There should be no nested sequential effects in a combined effect");
@@ -467,6 +519,17 @@ public abstract class CombinedVibrationEffect implements Parcelable {
            }
        }

        @Override
        public boolean hasVibrator(int vibratorId) {
            final int effectCount = mEffects.size();
            for (int i = 0; i < effectCount; i++) {
                if (mEffects.get(i).hasVibrator(vibratorId)) {
                    return true;
                }
            }
            return false;
        }

        @Override
        public boolean equals(Object o) {
            if (!(o instanceof Sequential)) {
+53 −31
Original line number Diff line number Diff line
@@ -317,7 +317,7 @@ public abstract class VibrationEffect implements Parcelable {
     */
    @TestApi
    public static VibrationEffect get(int effectId, boolean fallback) {
        VibrationEffect effect = new Prebaked(effectId, fallback);
        VibrationEffect effect = new Prebaked(effectId, fallback, EffectStrength.MEDIUM);
        effect.validate();
        return effect;
    }
@@ -792,22 +792,30 @@ public abstract class VibrationEffect implements Parcelable {
    public static class Prebaked extends VibrationEffect implements Parcelable {
        private final int mEffectId;
        private final boolean mFallback;

        private int mEffectStrength;
        private final int mEffectStrength;
        @Nullable
        private final VibrationEffect mFallbackEffect;

        public Prebaked(Parcel in) {
            this(in.readInt(), in.readByte() != 0, in.readInt());
            mEffectId = in.readInt();
            mFallback = in.readByte() != 0;
            mEffectStrength = in.readInt();
            mFallbackEffect = in.readParcelable(VibrationEffect.class.getClassLoader());
        }

        public Prebaked(int effectId, boolean fallback) {
            this(effectId, fallback, EffectStrength.MEDIUM);
        public Prebaked(int effectId, boolean fallback, int effectStrength) {
            mEffectId = effectId;
            mFallback = fallback;
            mEffectStrength = effectStrength;
            mFallbackEffect = null;
        }

        /** @hide */
        public Prebaked(int effectId, boolean fallback, int effectStrength) {
        public Prebaked(int effectId, int effectStrength, @NonNull VibrationEffect fallbackEffect) {
            mEffectId = effectId;
            mFallback = fallback;
            mFallback = true;
            mEffectStrength = effectStrength;
            mFallbackEffect = fallbackEffect;
        }

        public int getId() {
@@ -829,26 +837,27 @@ public abstract class VibrationEffect implements Parcelable {

        /** @hide */
        @Override
        public VibrationEffect resolve(int defaultAmplitude) {
            // Prebaked effects already have default amplitude set, so ignore this.
        public Prebaked resolve(int defaultAmplitude) {
            if (mFallbackEffect != null) {
                VibrationEffect resolvedFallback = mFallbackEffect.resolve(defaultAmplitude);
                if (!mFallbackEffect.equals(resolvedFallback)) {
                    return new Prebaked(mEffectId, mEffectStrength, resolvedFallback);
                }
            }
            return this;
        }

        /** @hide */
        @Override
        public Prebaked scale(float scaleFactor) {
            // Prebaked effects cannot be scaled, so ignore this.
            return this;
            if (mFallbackEffect != null) {
                VibrationEffect scaledFallback = mFallbackEffect.scale(scaleFactor);
                if (!mFallbackEffect.equals(scaledFallback)) {
                    return new Prebaked(mEffectId, mEffectStrength, scaledFallback);
                }

        /**
         * Set the effect strength of the prebaked effect.
         */
        public void setEffectStrength(int strength) {
            if (!isValidEffectStrength(strength)) {
                throw new IllegalArgumentException("Invalid effect strength: " + strength);
            }
            mEffectStrength = strength;
            // Prebaked effect strength cannot be scaled with this method.
            return this;
        }

        /**
@@ -858,6 +867,16 @@ public abstract class VibrationEffect implements Parcelable {
            return mEffectStrength;
        }

        /**
         * Return the fallback effect, if set.
         *
         * @hide
         */
        @Nullable
        public VibrationEffect getFallbackEffect() {
            return mFallbackEffect;
        }

        private static boolean isValidEffectStrength(int strength) {
            switch (strength) {
                case EffectStrength.LIGHT:
@@ -901,15 +920,13 @@ public abstract class VibrationEffect implements Parcelable {
            VibrationEffect.Prebaked other = (VibrationEffect.Prebaked) o;
            return mEffectId == other.mEffectId
                && mFallback == other.mFallback
                && mEffectStrength == other.mEffectStrength;
                && mEffectStrength == other.mEffectStrength
                && Objects.equals(mFallbackEffect, other.mFallbackEffect);
        }

        @Override
        public int hashCode() {
            int result = 17;
            result += 37 * mEffectId;
            result += 37 * mEffectStrength;
            return result;
            return Objects.hash(mEffectId, mFallback, mEffectStrength, mFallbackEffect);
        }

        @Override
@@ -917,6 +934,7 @@ public abstract class VibrationEffect implements Parcelable {
            return "Prebaked{mEffectId=" + mEffectId
                + ", mEffectStrength=" + mEffectStrength
                + ", mFallback=" + mFallback
                + ", mFallbackEffect=" + mFallbackEffect
                + "}";
        }

@@ -927,6 +945,7 @@ public abstract class VibrationEffect implements Parcelable {
            out.writeInt(mEffectId);
            out.writeByte((byte) (mFallback ? 1 : 0));
            out.writeInt(mEffectStrength);
            out.writeParcelable(mFallbackEffect, flags);
        }

        public static final @NonNull Parcelable.Creator<Prebaked> CREATOR =
@@ -990,8 +1009,10 @@ public abstract class VibrationEffect implements Parcelable {
                // Just return this if there's no scaling to be done.
                return this;
            }
            final int primitiveCount = mPrimitiveEffects.size();
            List<Composition.PrimitiveEffect> scaledPrimitives = new ArrayList<>();
            for (Composition.PrimitiveEffect primitive : mPrimitiveEffects) {
            for (int i = 0; i < primitiveCount; i++) {
                Composition.PrimitiveEffect primitive = mPrimitiveEffects.get(i);
                scaledPrimitives.add(new Composition.PrimitiveEffect(
                        primitive.id, scale(primitive.scale, scaleFactor), primitive.delay));
            }
@@ -1001,11 +1022,12 @@ public abstract class VibrationEffect implements Parcelable {
        /** @hide */
        @Override
        public void validate() {
            for (Composition.PrimitiveEffect effect : mPrimitiveEffects) {
                Composition.checkPrimitive(effect.id);
                Preconditions.checkArgumentInRange(
                        effect.scale, 0.0f, 1.0f, "scale");
                Preconditions.checkArgumentNonNegative(effect.delay,
            final int primitiveCount = mPrimitiveEffects.size();
            for (int i = 0; i < primitiveCount; i++) {
                Composition.PrimitiveEffect primitive = mPrimitiveEffects.get(i);
                Composition.checkPrimitive(primitive.id);
                Preconditions.checkArgumentInRange(primitive.scale, 0.0f, 1.0f, "scale");
                Preconditions.checkArgumentNonNegative(primitive.delay,
                        "Primitive delay must be zero or positive");
            }
        }
+52 −6
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@ import android.platform.test.annotations.Presubmit;

import com.android.internal.R;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnitRunner;
@@ -168,15 +169,30 @@ public class VibrationEffectTest {
    }

    @Test
    public void testScalePrebaked_ignoresScaleAndReturnsSameEffect() {
        VibrationEffect initial = VibrationEffect.get(VibrationEffect.RINGTONES[1]);
        assertSame(initial, initial.scale(0.5f));
    public void testScalePrebaked_scalesFallbackEffect() {
        VibrationEffect.Prebaked prebaked =
                (VibrationEffect.Prebaked) VibrationEffect.get(VibrationEffect.RINGTONES[1]);
        assertSame(prebaked, prebaked.scale(0.5f));

        prebaked = new VibrationEffect.Prebaked(VibrationEffect.EFFECT_CLICK,
                VibrationEffect.EFFECT_STRENGTH_MEDIUM, TEST_ONE_SHOT);
        VibrationEffect.OneShot scaledFallback =
                (VibrationEffect.OneShot) prebaked.scale(0.5f).getFallbackEffect();
        assertEquals(34, scaledFallback.getAmplitude(), AMPLITUDE_SCALE_TOLERANCE);
    }

    @Test
    public void testResolvePrebaked_ignoresDefaultAmplitudeAndReturnsSameEffect() {
        VibrationEffect initial = VibrationEffect.get(VibrationEffect.RINGTONES[1]);
        assertSame(initial, initial.resolve(1000));
    public void testResolvePrebaked_resolvesFallbackEffectIfSet() {
        VibrationEffect.Prebaked prebaked =
                (VibrationEffect.Prebaked) VibrationEffect.get(VibrationEffect.RINGTONES[1]);
        assertSame(prebaked, prebaked.resolve(1000));

        prebaked = new VibrationEffect.Prebaked(VibrationEffect.EFFECT_CLICK,
                VibrationEffect.EFFECT_STRENGTH_MEDIUM,
                VibrationEffect.createOneShot(1, VibrationEffect.DEFAULT_AMPLITUDE));
        VibrationEffect.OneShot resolvedFallback =
                (VibrationEffect.OneShot) prebaked.resolve(10).getFallbackEffect();
        assertEquals(10, resolvedFallback.getAmplitude());
    }

    @Test
@@ -352,6 +368,36 @@ public class VibrationEffectTest {
                INTENSITY_SCALE_TOLERANCE);
    }

    @Test
    public void getEffectStrength_returnsValueFromConstructor() {
        VibrationEffect.Prebaked effect = new VibrationEffect.Prebaked(VibrationEffect.EFFECT_CLICK,
                VibrationEffect.EFFECT_STRENGTH_LIGHT, null);
        Assert.assertEquals(VibrationEffect.EFFECT_STRENGTH_LIGHT, effect.getEffectStrength());
    }

    @Test
    public void getFallbackEffect_withFallbackDisabled_isNull() {
        VibrationEffect fallback = VibrationEffect.createOneShot(100, 100);
        VibrationEffect.Prebaked effect = new VibrationEffect.Prebaked(VibrationEffect.EFFECT_CLICK,
                false, VibrationEffect.EFFECT_STRENGTH_LIGHT);
        Assert.assertNull(effect.getFallbackEffect());
    }

    @Test
    public void getFallbackEffect_withoutEffectSet_isNull() {
        VibrationEffect.Prebaked effect = new VibrationEffect.Prebaked(VibrationEffect.EFFECT_CLICK,
                true, VibrationEffect.EFFECT_STRENGTH_LIGHT);
        Assert.assertNull(effect.getFallbackEffect());
    }

    @Test
    public void getFallbackEffect_withFallback_returnsValueFromConstructor() {
        VibrationEffect fallback = VibrationEffect.createOneShot(100, 100);
        VibrationEffect.Prebaked effect = new VibrationEffect.Prebaked(VibrationEffect.EFFECT_CLICK,
                VibrationEffect.EFFECT_STRENGTH_LIGHT, fallback);
        Assert.assertEquals(fallback, effect.getFallbackEffect());
    }

    private Resources mockRingtoneResources() {
        return mockRingtoneResources(new String[] {
                RINGTONE_URI_1,
+74 −390

File changed.

Preview size limit exceeded, changes collapsed.

Loading