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

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

Update vendor VibrationEffect to support scale factor

Update VibrationEffect.VendorEffect to support both a continuous scale
factor and an adaptive scale parameter, to map to the actual 2-tier
scaling logic of the platform.

This renames VibrationEffect.scaleLinearly to applyAdaptiveScale, to
make the method meaning explicit and allow the vendor implementation
that will forward the parameter provided by the vendor controller
service to the vibrator HAL.

The vendor effect HAL parcelable now takes both effect strength and a
continuous scale factor parameter, so vendors can support user
settings-based scale more openly.

Bug: 345409060
Test: VibrationEffectTest & VibrationScalerTest
Flag: android.os.vibrator.vendor_vibration_effects
Change-Id: I0b3e00e7673fb069ae1f293979d1942b9ad1bafb
parent ca6734ab
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -2579,9 +2579,10 @@ package android.os {

  @FlaggedApi("android.os.vibrator.vendor_vibration_effects") public static final class VibrationEffect.VendorEffect extends android.os.VibrationEffect {
    method @Nullable public long[] computeCreateWaveformOffOnTimingsOrNull();
    method public float getAdaptiveScale();
    method public long getDuration();
    method public int getEffectStrength();
    method public float getLinearScale();
    method public float getScale();
    method @NonNull public android.os.PersistableBundle getVendorData();
    method public void writeToParcel(@NonNull android.os.Parcel, int);
    field @NonNull public static final android.os.Parcelable.Creator<android.os.VibrationEffect.VendorEffect> CREATOR;
+38 −22
Original line number Diff line number Diff line
@@ -346,7 +346,7 @@ public abstract class VibrationEffect implements Parcelable {
    @RequiresPermission(android.Manifest.permission.VIBRATE_VENDOR_EFFECTS)
    public static VibrationEffect createVendorEffect(@NonNull PersistableBundle effect) {
        VibrationEffect vendorEffect = new VendorEffect(effect, VendorEffect.DEFAULT_STRENGTH,
                VendorEffect.DEFAULT_SCALE);
                VendorEffect.DEFAULT_SCALE, VendorEffect.DEFAULT_SCALE);
        vendorEffect.validate();
        return vendorEffect;
    }
@@ -623,7 +623,7 @@ public abstract class VibrationEffect implements Parcelable {
     * @hide
     */
    @NonNull
    public abstract VibrationEffect scaleLinearly(float scaleFactor);
    public abstract VibrationEffect applyAdaptiveScale(float scaleFactor);

    /**
     * Ensures that the effect is repeating indefinitely or not. This is a lossy operation and
@@ -948,7 +948,7 @@ public abstract class VibrationEffect implements Parcelable {
        /** @hide */
        @NonNull
        @Override
        public Composed scaleLinearly(float scaleFactor) {
        public Composed applyAdaptiveScale(float scaleFactor) {
            return applyToSegments(VibrationEffectSegment::scaleLinearly, scaleFactor);
        }

@@ -1100,21 +1100,23 @@ public abstract class VibrationEffect implements Parcelable {

        private final PersistableBundle mVendorData;
        private final int mEffectStrength;
        private final float mLinearScale;
        private final float mScale;
        private final float mAdaptiveScale;

        /** @hide */
        VendorEffect(@NonNull Parcel in) {
            this(Objects.requireNonNull(
                    in.readPersistableBundle(VibrationEffect.class.getClassLoader())),
                    in.readInt(), in.readFloat());
                    in.readInt(), in.readFloat(), in.readFloat());
        }

        /** @hide */
        public VendorEffect(@NonNull PersistableBundle vendorData, int effectStrength,
                float linearScale) {
                float scale, float adaptiveScale) {
            mVendorData = vendorData;
            mEffectStrength = effectStrength;
            mLinearScale = linearScale;
            mScale = scale;
            mAdaptiveScale = adaptiveScale;
        }

        @NonNull
@@ -1126,8 +1128,12 @@ public abstract class VibrationEffect implements Parcelable {
            return mEffectStrength;
        }

        public float getLinearScale() {
            return mLinearScale;
        public float getScale() {
            return mScale;
        }

        public float getAdaptiveScale() {
            return mAdaptiveScale;
        }

        /** @hide */
@@ -1175,7 +1181,8 @@ public abstract class VibrationEffect implements Parcelable {
            if (mEffectStrength == effectStrength) {
                return this;
            }
            VendorEffect updated = new VendorEffect(mVendorData, effectStrength, mLinearScale);
            VendorEffect updated = new VendorEffect(mVendorData, effectStrength, mScale,
                    mAdaptiveScale);
            updated.validate();
            return updated;
        }
@@ -1184,18 +1191,24 @@ public abstract class VibrationEffect implements Parcelable {
        @NonNull
        @Override
        public VendorEffect scale(float scaleFactor) {
            // Vendor effect strength cannot be scaled with this method.
            if (Float.compare(mScale, scaleFactor) == 0) {
                return this;
            }
            VendorEffect updated = new VendorEffect(mVendorData, mEffectStrength, scaleFactor,
                    mAdaptiveScale);
            updated.validate();
            return updated;
        }

        /** @hide */
        @NonNull
        @Override
        public VibrationEffect scaleLinearly(float scaleFactor) {
            if (Float.compare(mLinearScale, scaleFactor) == 0) {
        public VibrationEffect applyAdaptiveScale(float scaleFactor) {
            if (Float.compare(mAdaptiveScale, scaleFactor) == 0) {
                return this;
            }
            VendorEffect updated = new VendorEffect(mVendorData, mEffectStrength, scaleFactor);
            VendorEffect updated = new VendorEffect(mVendorData, mEffectStrength, mScale,
                    scaleFactor);
            updated.validate();
            return updated;
        }
@@ -1216,29 +1229,31 @@ public abstract class VibrationEffect implements Parcelable {
                return false;
            }
            return mEffectStrength == other.mEffectStrength
                    && (Float.compare(mLinearScale, other.mLinearScale) == 0)
                    && (Float.compare(mScale, other.mScale) == 0)
                    && (Float.compare(mAdaptiveScale, other.mAdaptiveScale) == 0)
                    && isPersistableBundleEquals(mVendorData, other.mVendorData);
        }

        @Override
        public int hashCode() {
            // PersistableBundle does not implement hashCode, so use its size as a shortcut.
            return Objects.hash(mVendorData.size(), mEffectStrength, mLinearScale);
            return Objects.hash(mVendorData.size(), mEffectStrength, mScale, mAdaptiveScale);
        }

        @Override
        public String toString() {
            return String.format(Locale.ROOT,
                    "VendorEffect{vendorData=%s, strength=%s, scale=%.2f}",
                    mVendorData, effectStrengthToString(mEffectStrength), mLinearScale);
                    "VendorEffect{vendorData=%s, strength=%s, scale=%.2f, adaptiveScale=%.2f}",
                    mVendorData, effectStrengthToString(mEffectStrength), mScale, mAdaptiveScale);
        }

        /** @hide */
        @Override
        public String toDebugString() {
            return String.format(Locale.ROOT, "vendorEffect=%s, strength=%s, scale=%.2f",
            return String.format(Locale.ROOT,
                    "vendorEffect=%s, strength=%s, scale=%.2f, adaptiveScale=%.2f",
                    mVendorData.toShortString(), effectStrengthToString(mEffectStrength),
                    mLinearScale);
                    mScale, mAdaptiveScale);
        }

        @Override
@@ -1251,7 +1266,8 @@ public abstract class VibrationEffect implements Parcelable {
            out.writeInt(PARCEL_TOKEN_VENDOR_EFFECT);
            out.writePersistableBundle(mVendorData);
            out.writeInt(mEffectStrength);
            out.writeFloat(mLinearScale);
            out.writeFloat(mScale);
            out.writeFloat(mAdaptiveScale);
        }

        /**
+70 −6
Original line number Diff line number Diff line
@@ -60,7 +60,7 @@ import java.util.Arrays;

@RunWith(MockitoJUnitRunner.class)
public class VibrationEffectTest {

    private static final float TOLERANCE = 1e-2f;
    private static final String RINGTONE_URI_1 = "content://test/system/ringtone_1";
    private static final String RINGTONE_URI_2 = "content://test/system/ringtone_2";
    private static final String RINGTONE_URI_3 = "content://test/system/ringtone_3";
@@ -709,7 +709,7 @@ public class VibrationEffectTest {
    @Test
    public void testScaleWaveform() {
        VibrationEffect scaledUp = TEST_WAVEFORM.scale(1.5f);
        assertEquals(1f, getStepSegment(scaledUp, 0).getAmplitude(), 1e-5f);
        assertEquals(1f, getStepSegment(scaledUp, 0).getAmplitude(), TOLERANCE);

        VibrationEffect scaledDown = TEST_WAVEFORM.scale(0.5f);
        assertTrue(1f > getStepSegment(scaledDown, 0).getAmplitude());
@@ -731,11 +731,11 @@ public class VibrationEffectTest {
    public void testScaleVendorEffect() {
        VibrationEffect effect = VibrationEffect.createVendorEffect(createNonEmptyBundle());

        VibrationEffect scaledUp = effect.scale(1.5f);
        assertEquals(effect, scaledUp);
        VibrationEffect.VendorEffect scaledUp = (VibrationEffect.VendorEffect) effect.scale(1.5f);
        assertEquals(1.5f, scaledUp.getScale());

        VibrationEffect scaledDown = effect.scale(0.5f);
        assertEquals(effect, scaledDown);
        VibrationEffect.VendorEffect scaledDown = (VibrationEffect.VendorEffect) effect.scale(0.5f);
        assertEquals(0.5f, scaledDown.getScale());
    }

    @Test
@@ -754,6 +754,70 @@ public class VibrationEffectTest {
        assertTrue(100 / 255f > getStepSegment(scaledDown, 1).getAmplitude());
    }

    @Test
    public void testApplyAdaptiveScaleOneShot() {
        VibrationEffect oneShot = VibrationEffect.createOneShot(TEST_TIMING, /* amplitude= */ 100);

        VibrationEffect scaledUp = oneShot.applyAdaptiveScale(1.5f);
        assertThat(getStepSegment(scaledUp, 0).getAmplitude()).isWithin(TOLERANCE).of(150 / 255f);

        VibrationEffect scaledDown = oneShot.applyAdaptiveScale(0.5f);
        assertThat(getStepSegment(scaledDown, 0).getAmplitude()).isWithin(TOLERANCE).of(50 / 255f);
    }

    @Test
    public void testApplyAdaptiveScaleWaveform() {
        VibrationEffect waveform = VibrationEffect.createWaveform(
                new long[] { 100, 100 }, new int[] { 10, 0 }, -1);

        VibrationEffect scaledUp = waveform.applyAdaptiveScale(1.5f);
        assertThat(getStepSegment(scaledUp, 0).getAmplitude()).isWithin(TOLERANCE).of(15 / 255f);

        VibrationEffect scaledDown = waveform.applyAdaptiveScale(0.5f);
        assertThat(getStepSegment(scaledDown, 0).getAmplitude()).isWithin(TOLERANCE).of(5 / 255f);
    }

    @Test
    public void testApplyAdaptiveScalePrebaked() {
        VibrationEffect effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);

        VibrationEffect scaledUp = effect.applyAdaptiveScale(1.5f);
        assertEquals(effect, scaledUp);

        VibrationEffect scaledDown = effect.applyAdaptiveScale(0.5f);
        assertEquals(effect, scaledDown);
    }

    @Test
    @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
    public void testApplyAdaptiveScaleVendorEffect() {
        VibrationEffect effect = VibrationEffect.createVendorEffect(createNonEmptyBundle());

        VibrationEffect.VendorEffect scaledUp =
                (VibrationEffect.VendorEffect) effect.applyAdaptiveScale(1.5f);
        assertEquals(1.5f, scaledUp.getAdaptiveScale());

        VibrationEffect.VendorEffect scaledDown =
                (VibrationEffect.VendorEffect) effect.applyAdaptiveScale(0.5f);
        assertEquals(0.5f, scaledDown.getAdaptiveScale());
    }

    @Test
    public void testApplyAdaptiveScaleComposed() {
        VibrationEffect effect = VibrationEffect.startComposition()
                .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 0.5f, 1)
                .addEffect(VibrationEffect.createOneShot(TEST_TIMING, /* amplitude= */ 100))
                .compose();

        VibrationEffect scaledUp = effect.applyAdaptiveScale(1.5f);
        assertThat(getPrimitiveSegment(scaledUp, 0).getScale()).isWithin(TOLERANCE).of(0.75f);
        assertThat(getStepSegment(scaledUp, 1).getAmplitude()).isWithin(TOLERANCE).of(150 / 255f);

        VibrationEffect scaledDown = effect.applyAdaptiveScale(0.5f);
        assertThat(getPrimitiveSegment(scaledDown, 0).getScale()).isWithin(TOLERANCE).of(0.25f);
        assertThat(getStepSegment(scaledDown, 1).getAmplitude()).isWithin(TOLERANCE).of(50 / 255f);
    }

    @Test
    public void testApplyEffectStrengthToOneShotWaveformAndPrimitives() {
        VibrationEffect oneShot = VibrationEffect.createOneShot(100, 100);
+2 −1
Original line number Diff line number Diff line
@@ -134,7 +134,8 @@ final class VibrationScaler {
        return effect.resolve(mDefaultVibrationAmplitude)
                .applyEffectStrength(newEffectStrength)
                .scale(scaleFactor)
                .scaleLinearly(adaptiveScale);
                // Make sure this is the last one so it is applied on top of the settings scaling.
                .applyAdaptiveScale(adaptiveScale);
    }

    /**
+6 −5
Original line number Diff line number Diff line
@@ -279,8 +279,8 @@ final class VibratorController {
                vendorEffect.getVendorData().writeToParcel(vendorData, /* flags= */ 0);
                vendorData.setDataPosition(0);
                long duration = mNativeWrapper.performVendorEffect(vendorData,
                        vendorEffect.getEffectStrength(), vendorEffect.getLinearScale(),
                        vibrationId);
                        vendorEffect.getEffectStrength(), vendorEffect.getScale(),
                        vendorEffect.getAdaptiveScale(), vibrationId);
                if (duration > 0) {
                    mCurrentAmplitude = -1;
                    notifyListenerOnVibrating(true);
@@ -459,7 +459,7 @@ final class VibratorController {
                long vibrationId);

        private static native long performVendorEffect(long nativePtr, Parcel vendorData,
                long strength, float scale, long vibrationId);
                long strength, float scale, float adaptiveScale, long vibrationId);

        private static native long performComposedEffect(long nativePtr, PrimitiveSegment[] effect,
                long vibrationId);
@@ -518,8 +518,9 @@ final class VibratorController {

        /** Turns vibrator on to perform a vendor-specific effect. */
        public long performVendorEffect(Parcel vendorData, long strength, float scale,
                long vibrationId) {
            return performVendorEffect(mNativePtr, vendorData, strength, scale, vibrationId);
                float adaptiveScale, long vibrationId) {
            return performVendorEffect(mNativePtr, vendorData, strength, scale, adaptiveScale,
                    vibrationId);
        }

        /** Turns vibrator on to perform effect composed of give primitives effect. */
Loading