Loading core/java/android/os/ExternalVibrationScale.aidl +12 −0 Original line number Diff line number Diff line Loading @@ -33,11 +33,23 @@ parcelable ExternalVibrationScale { SCALE_VERY_HIGH = 2 } // TODO(b/345186129): remove this once we finish migrating to scale factor. /** * The scale level that will be applied to external vibrations. */ ScaleLevel scaleLevel = ScaleLevel.SCALE_NONE; /** * The scale factor that will be applied to external vibrations. * * Values in (0,1) will scale down the vibrations, values > 1 will scale up vibrations within * hardware limits. A zero scale factor indicates the external vibration should be muted. * * TODO(b/345186129): update this once we finish migrating, negative should not be expected. * Negative values should be ignored in favour of the legacy ScaleLevel. */ float scaleFactor = -1f; // undefined /** * The adaptive haptics scale that will be applied to external vibrations. */ Loading core/java/android/os/VibrationEffect.java +9 −0 Original line number Diff line number Diff line Loading @@ -663,6 +663,15 @@ public abstract class VibrationEffect implements Parcelable { * @hide */ public static float scale(float intensity, float scaleFactor) { if (Flags.hapticsScaleV2Enabled()) { if (Float.compare(scaleFactor, 1) <= 0 || Float.compare(intensity, 0) == 0) { // Scaling down or scaling zero intensity is straightforward. return scaleFactor * intensity; } // Using S * x / (1 + (S - 1) * x^2) as the scale up function to converge to 1.0. return (scaleFactor * intensity) / (1 + (scaleFactor - 1) * intensity * intensity); } // Applying gamma correction to the scale factor, which is the same as encoding the input // value, scaling it, then decoding the scaled value. float scale = MathUtils.pow(scaleFactor, 1f / SCALE_GAMMA); Loading core/java/android/os/vibrator/VibrationConfig.java +45 −7 Original line number Diff line number Diff line Loading @@ -49,8 +49,22 @@ import java.util.Arrays; */ public class VibrationConfig { /** * Hardcoded default scale level gain to be applied between each scale level to define their * scale factor value. * * <p>Default gain defined as 3 dBs. */ private static final float DEFAULT_SCALE_LEVEL_GAIN = 1.4f; /** * Hardcoded default amplitude to be used when device config is invalid, i.e. not in [1,255]. */ private static final int DEFAULT_AMPLITUDE = 255; // TODO(b/191150049): move these to vibrator static config file private final float mHapticChannelMaxVibrationAmplitude; private final int mDefaultVibrationAmplitude; private final int mRampStepDurationMs; private final int mRampDownDurationMs; private final int mRequestVibrationParamsTimeoutMs; Loading @@ -75,8 +89,10 @@ public class VibrationConfig { /** @hide */ public VibrationConfig(@Nullable Resources resources) { mDefaultVibrationAmplitude = resources.getInteger( com.android.internal.R.integer.config_defaultVibrationAmplitude); mHapticChannelMaxVibrationAmplitude = loadFloat(resources, com.android.internal.R.dimen.config_hapticChannelMaxVibrationAmplitude, 0); com.android.internal.R.dimen.config_hapticChannelMaxVibrationAmplitude); mRampDownDurationMs = loadInteger(resources, com.android.internal.R.integer.config_vibrationWaveformRampDownDuration, 0); mRampStepDurationMs = loadInteger(resources, Loading @@ -87,9 +103,9 @@ public class VibrationConfig { com.android.internal.R.array.config_requestVibrationParamsForUsages); mIgnoreVibrationsOnWirelessCharger = loadBoolean(resources, com.android.internal.R.bool.config_ignoreVibrationsOnWirelessCharger, false); com.android.internal.R.bool.config_ignoreVibrationsOnWirelessCharger); mKeyboardVibrationSettingsSupported = loadBoolean(resources, com.android.internal.R.bool.config_keyboardVibrationSettingsSupported, false); com.android.internal.R.bool.config_keyboardVibrationSettingsSupported); mDefaultAlarmVibrationIntensity = loadDefaultIntensity(resources, com.android.internal.R.integer.config_defaultAlarmVibrationIntensity); Loading @@ -115,16 +131,16 @@ public class VibrationConfig { return value; } private static float loadFloat(@Nullable Resources res, int resId, float defaultValue) { return res != null ? res.getFloat(resId) : defaultValue; private static float loadFloat(@Nullable Resources res, int resId) { return res != null ? res.getFloat(resId) : 0f; } private static int loadInteger(@Nullable Resources res, int resId, int defaultValue) { return res != null ? res.getInteger(resId) : defaultValue; } private static boolean loadBoolean(@Nullable Resources res, int resId, boolean defaultValue) { return res != null ? res.getBoolean(resId) : defaultValue; private static boolean loadBoolean(@Nullable Resources res, int resId) { return res != null && res.getBoolean(resId); } private static int[] loadIntArray(@Nullable Resources res, int resId) { Loading @@ -144,6 +160,26 @@ public class VibrationConfig { return mHapticChannelMaxVibrationAmplitude; } /** * Return the device default vibration amplitude value to replace the * {@link android.os.VibrationEffect#DEFAULT_AMPLITUDE} constant. */ public int getDefaultVibrationAmplitude() { if (mDefaultVibrationAmplitude < 1 || mDefaultVibrationAmplitude > 255) { return DEFAULT_AMPLITUDE; } return mDefaultVibrationAmplitude; } /** * Return the device default gain to be applied between scale levels to define the scale factor * for each level. */ public float getDefaultVibrationScaleLevelGain() { // TODO(b/356407380): add device config for this return DEFAULT_SCALE_LEVEL_GAIN; } /** * The duration, in milliseconds, that should be applied to the ramp to turn off the vibrator * when a vibration is cancelled or finished at non-zero amplitude. Loading Loading @@ -233,6 +269,7 @@ public class VibrationConfig { public String toString() { return "VibrationConfig{" + "mIgnoreVibrationsOnWirelessCharger=" + mIgnoreVibrationsOnWirelessCharger + ", mDefaultVibrationAmplitude=" + mDefaultVibrationAmplitude + ", mHapticChannelMaxVibrationAmplitude=" + mHapticChannelMaxVibrationAmplitude + ", mRampStepDurationMs=" + mRampStepDurationMs + ", mRampDownDurationMs=" + mRampDownDurationMs Loading @@ -258,6 +295,7 @@ public class VibrationConfig { pw.println("VibrationConfig:"); pw.increaseIndent(); pw.println("ignoreVibrationsOnWirelessCharger = " + mIgnoreVibrationsOnWirelessCharger); pw.println("defaultVibrationAmplitude = " + mDefaultVibrationAmplitude); pw.println("hapticChannelMaxAmplitude = " + mHapticChannelMaxVibrationAmplitude); pw.println("rampStepDurationMs = " + mRampStepDurationMs); pw.println("rampDownDurationMs = " + mRampDownDurationMs); Loading core/java/android/os/vibrator/flags.aconfig +10 −0 Original line number Diff line number Diff line Loading @@ -103,3 +103,13 @@ flag { purpose: PURPOSE_FEATURE } } flag { namespace: "haptics" name: "haptics_scale_v2_enabled" description: "Enables new haptics scaling function across all usages" bug: "345186129" metadata { purpose: PURPOSE_FEATURE } } core/tests/vibrator/src/android/os/vibrator/PrimitiveSegmentTest.java +43 −2 Original line number Diff line number Diff line Loading @@ -27,7 +27,11 @@ import android.hardware.vibrator.IVibrator; import android.os.Parcel; import android.os.VibrationEffect; import android.os.VibratorInfo; import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; Loading @@ -36,6 +40,9 @@ import org.junit.runners.JUnit4; public class PrimitiveSegmentTest { private static final float TOLERANCE = 1e-2f; @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); @Test public void testCreation() { PrimitiveSegment primitive = new PrimitiveSegment( Loading Loading @@ -87,7 +94,8 @@ public class PrimitiveSegmentTest { } @Test public void testScale_fullPrimitiveScaleValue() { @DisableFlags(Flags.FLAG_HAPTICS_SCALE_V2_ENABLED) public void testScale_withLegacyScaling_fullPrimitiveScaleValue() { PrimitiveSegment initial = new PrimitiveSegment( VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 0); Loading @@ -102,7 +110,24 @@ public class PrimitiveSegmentTest { } @Test public void testScale_halfPrimitiveScaleValue() { @EnableFlags(Flags.FLAG_HAPTICS_SCALE_V2_ENABLED) public void testScale_withScalingV2_fullPrimitiveScaleValue() { PrimitiveSegment initial = new PrimitiveSegment( VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 0); assertEquals(1f, initial.scale(1).getScale(), TOLERANCE); assertEquals(0.5f, initial.scale(0.5f).getScale(), TOLERANCE); // The original value was not scaled up, so this only scales it down. assertEquals(1f, initial.scale(1.5f).getScale(), TOLERANCE); assertEquals(2 / 3f, initial.scale(1.5f).scale(2 / 3f).getScale(), TOLERANCE); // Does not restore to the exact original value because scale up is a bit offset. assertEquals(0.8f, initial.scale(0.8f).getScale(), TOLERANCE); assertEquals(0.86f, initial.scale(0.8f).scale(1.25f).getScale(), TOLERANCE); } @Test @DisableFlags(Flags.FLAG_HAPTICS_SCALE_V2_ENABLED) public void testScale_withLegacyScaling_halfPrimitiveScaleValue() { PrimitiveSegment initial = new PrimitiveSegment( VibrationEffect.Composition.PRIMITIVE_CLICK, 0.5f, 0); Loading @@ -116,6 +141,22 @@ public class PrimitiveSegmentTest { assertEquals(0.5f, initial.scale(0.8f).scale(1.25f).getScale(), TOLERANCE); } @Test @EnableFlags(Flags.FLAG_HAPTICS_SCALE_V2_ENABLED) public void testScale_withScalingV2_halfPrimitiveScaleValue() { PrimitiveSegment initial = new PrimitiveSegment( VibrationEffect.Composition.PRIMITIVE_CLICK, 0.5f, 0); assertEquals(0.5f, initial.scale(1).getScale(), TOLERANCE); assertEquals(0.25f, initial.scale(0.5f).getScale(), TOLERANCE); // The original value was not scaled up, so this only scales it down. assertEquals(0.66f, initial.scale(1.5f).getScale(), TOLERANCE); assertEquals(0.44f, initial.scale(1.5f).scale(2 / 3f).getScale(), TOLERANCE); // Does not restore to the exact original value because scale up is a bit offset. assertEquals(0.4f, initial.scale(0.8f).getScale(), TOLERANCE); assertEquals(0.48f, initial.scale(0.8f).scale(1.25f).getScale(), TOLERANCE); } @Test public void testScale_zeroPrimitiveScaleValue() { PrimitiveSegment initial = new PrimitiveSegment( Loading Loading
core/java/android/os/ExternalVibrationScale.aidl +12 −0 Original line number Diff line number Diff line Loading @@ -33,11 +33,23 @@ parcelable ExternalVibrationScale { SCALE_VERY_HIGH = 2 } // TODO(b/345186129): remove this once we finish migrating to scale factor. /** * The scale level that will be applied to external vibrations. */ ScaleLevel scaleLevel = ScaleLevel.SCALE_NONE; /** * The scale factor that will be applied to external vibrations. * * Values in (0,1) will scale down the vibrations, values > 1 will scale up vibrations within * hardware limits. A zero scale factor indicates the external vibration should be muted. * * TODO(b/345186129): update this once we finish migrating, negative should not be expected. * Negative values should be ignored in favour of the legacy ScaleLevel. */ float scaleFactor = -1f; // undefined /** * The adaptive haptics scale that will be applied to external vibrations. */ Loading
core/java/android/os/VibrationEffect.java +9 −0 Original line number Diff line number Diff line Loading @@ -663,6 +663,15 @@ public abstract class VibrationEffect implements Parcelable { * @hide */ public static float scale(float intensity, float scaleFactor) { if (Flags.hapticsScaleV2Enabled()) { if (Float.compare(scaleFactor, 1) <= 0 || Float.compare(intensity, 0) == 0) { // Scaling down or scaling zero intensity is straightforward. return scaleFactor * intensity; } // Using S * x / (1 + (S - 1) * x^2) as the scale up function to converge to 1.0. return (scaleFactor * intensity) / (1 + (scaleFactor - 1) * intensity * intensity); } // Applying gamma correction to the scale factor, which is the same as encoding the input // value, scaling it, then decoding the scaled value. float scale = MathUtils.pow(scaleFactor, 1f / SCALE_GAMMA); Loading
core/java/android/os/vibrator/VibrationConfig.java +45 −7 Original line number Diff line number Diff line Loading @@ -49,8 +49,22 @@ import java.util.Arrays; */ public class VibrationConfig { /** * Hardcoded default scale level gain to be applied between each scale level to define their * scale factor value. * * <p>Default gain defined as 3 dBs. */ private static final float DEFAULT_SCALE_LEVEL_GAIN = 1.4f; /** * Hardcoded default amplitude to be used when device config is invalid, i.e. not in [1,255]. */ private static final int DEFAULT_AMPLITUDE = 255; // TODO(b/191150049): move these to vibrator static config file private final float mHapticChannelMaxVibrationAmplitude; private final int mDefaultVibrationAmplitude; private final int mRampStepDurationMs; private final int mRampDownDurationMs; private final int mRequestVibrationParamsTimeoutMs; Loading @@ -75,8 +89,10 @@ public class VibrationConfig { /** @hide */ public VibrationConfig(@Nullable Resources resources) { mDefaultVibrationAmplitude = resources.getInteger( com.android.internal.R.integer.config_defaultVibrationAmplitude); mHapticChannelMaxVibrationAmplitude = loadFloat(resources, com.android.internal.R.dimen.config_hapticChannelMaxVibrationAmplitude, 0); com.android.internal.R.dimen.config_hapticChannelMaxVibrationAmplitude); mRampDownDurationMs = loadInteger(resources, com.android.internal.R.integer.config_vibrationWaveformRampDownDuration, 0); mRampStepDurationMs = loadInteger(resources, Loading @@ -87,9 +103,9 @@ public class VibrationConfig { com.android.internal.R.array.config_requestVibrationParamsForUsages); mIgnoreVibrationsOnWirelessCharger = loadBoolean(resources, com.android.internal.R.bool.config_ignoreVibrationsOnWirelessCharger, false); com.android.internal.R.bool.config_ignoreVibrationsOnWirelessCharger); mKeyboardVibrationSettingsSupported = loadBoolean(resources, com.android.internal.R.bool.config_keyboardVibrationSettingsSupported, false); com.android.internal.R.bool.config_keyboardVibrationSettingsSupported); mDefaultAlarmVibrationIntensity = loadDefaultIntensity(resources, com.android.internal.R.integer.config_defaultAlarmVibrationIntensity); Loading @@ -115,16 +131,16 @@ public class VibrationConfig { return value; } private static float loadFloat(@Nullable Resources res, int resId, float defaultValue) { return res != null ? res.getFloat(resId) : defaultValue; private static float loadFloat(@Nullable Resources res, int resId) { return res != null ? res.getFloat(resId) : 0f; } private static int loadInteger(@Nullable Resources res, int resId, int defaultValue) { return res != null ? res.getInteger(resId) : defaultValue; } private static boolean loadBoolean(@Nullable Resources res, int resId, boolean defaultValue) { return res != null ? res.getBoolean(resId) : defaultValue; private static boolean loadBoolean(@Nullable Resources res, int resId) { return res != null && res.getBoolean(resId); } private static int[] loadIntArray(@Nullable Resources res, int resId) { Loading @@ -144,6 +160,26 @@ public class VibrationConfig { return mHapticChannelMaxVibrationAmplitude; } /** * Return the device default vibration amplitude value to replace the * {@link android.os.VibrationEffect#DEFAULT_AMPLITUDE} constant. */ public int getDefaultVibrationAmplitude() { if (mDefaultVibrationAmplitude < 1 || mDefaultVibrationAmplitude > 255) { return DEFAULT_AMPLITUDE; } return mDefaultVibrationAmplitude; } /** * Return the device default gain to be applied between scale levels to define the scale factor * for each level. */ public float getDefaultVibrationScaleLevelGain() { // TODO(b/356407380): add device config for this return DEFAULT_SCALE_LEVEL_GAIN; } /** * The duration, in milliseconds, that should be applied to the ramp to turn off the vibrator * when a vibration is cancelled or finished at non-zero amplitude. Loading Loading @@ -233,6 +269,7 @@ public class VibrationConfig { public String toString() { return "VibrationConfig{" + "mIgnoreVibrationsOnWirelessCharger=" + mIgnoreVibrationsOnWirelessCharger + ", mDefaultVibrationAmplitude=" + mDefaultVibrationAmplitude + ", mHapticChannelMaxVibrationAmplitude=" + mHapticChannelMaxVibrationAmplitude + ", mRampStepDurationMs=" + mRampStepDurationMs + ", mRampDownDurationMs=" + mRampDownDurationMs Loading @@ -258,6 +295,7 @@ public class VibrationConfig { pw.println("VibrationConfig:"); pw.increaseIndent(); pw.println("ignoreVibrationsOnWirelessCharger = " + mIgnoreVibrationsOnWirelessCharger); pw.println("defaultVibrationAmplitude = " + mDefaultVibrationAmplitude); pw.println("hapticChannelMaxAmplitude = " + mHapticChannelMaxVibrationAmplitude); pw.println("rampStepDurationMs = " + mRampStepDurationMs); pw.println("rampDownDurationMs = " + mRampDownDurationMs); Loading
core/java/android/os/vibrator/flags.aconfig +10 −0 Original line number Diff line number Diff line Loading @@ -103,3 +103,13 @@ flag { purpose: PURPOSE_FEATURE } } flag { namespace: "haptics" name: "haptics_scale_v2_enabled" description: "Enables new haptics scaling function across all usages" bug: "345186129" metadata { purpose: PURPOSE_FEATURE } }
core/tests/vibrator/src/android/os/vibrator/PrimitiveSegmentTest.java +43 −2 Original line number Diff line number Diff line Loading @@ -27,7 +27,11 @@ import android.hardware.vibrator.IVibrator; import android.os.Parcel; import android.os.VibrationEffect; import android.os.VibratorInfo; import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; Loading @@ -36,6 +40,9 @@ import org.junit.runners.JUnit4; public class PrimitiveSegmentTest { private static final float TOLERANCE = 1e-2f; @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); @Test public void testCreation() { PrimitiveSegment primitive = new PrimitiveSegment( Loading Loading @@ -87,7 +94,8 @@ public class PrimitiveSegmentTest { } @Test public void testScale_fullPrimitiveScaleValue() { @DisableFlags(Flags.FLAG_HAPTICS_SCALE_V2_ENABLED) public void testScale_withLegacyScaling_fullPrimitiveScaleValue() { PrimitiveSegment initial = new PrimitiveSegment( VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 0); Loading @@ -102,7 +110,24 @@ public class PrimitiveSegmentTest { } @Test public void testScale_halfPrimitiveScaleValue() { @EnableFlags(Flags.FLAG_HAPTICS_SCALE_V2_ENABLED) public void testScale_withScalingV2_fullPrimitiveScaleValue() { PrimitiveSegment initial = new PrimitiveSegment( VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 0); assertEquals(1f, initial.scale(1).getScale(), TOLERANCE); assertEquals(0.5f, initial.scale(0.5f).getScale(), TOLERANCE); // The original value was not scaled up, so this only scales it down. assertEquals(1f, initial.scale(1.5f).getScale(), TOLERANCE); assertEquals(2 / 3f, initial.scale(1.5f).scale(2 / 3f).getScale(), TOLERANCE); // Does not restore to the exact original value because scale up is a bit offset. assertEquals(0.8f, initial.scale(0.8f).getScale(), TOLERANCE); assertEquals(0.86f, initial.scale(0.8f).scale(1.25f).getScale(), TOLERANCE); } @Test @DisableFlags(Flags.FLAG_HAPTICS_SCALE_V2_ENABLED) public void testScale_withLegacyScaling_halfPrimitiveScaleValue() { PrimitiveSegment initial = new PrimitiveSegment( VibrationEffect.Composition.PRIMITIVE_CLICK, 0.5f, 0); Loading @@ -116,6 +141,22 @@ public class PrimitiveSegmentTest { assertEquals(0.5f, initial.scale(0.8f).scale(1.25f).getScale(), TOLERANCE); } @Test @EnableFlags(Flags.FLAG_HAPTICS_SCALE_V2_ENABLED) public void testScale_withScalingV2_halfPrimitiveScaleValue() { PrimitiveSegment initial = new PrimitiveSegment( VibrationEffect.Composition.PRIMITIVE_CLICK, 0.5f, 0); assertEquals(0.5f, initial.scale(1).getScale(), TOLERANCE); assertEquals(0.25f, initial.scale(0.5f).getScale(), TOLERANCE); // The original value was not scaled up, so this only scales it down. assertEquals(0.66f, initial.scale(1.5f).getScale(), TOLERANCE); assertEquals(0.44f, initial.scale(1.5f).scale(2 / 3f).getScale(), TOLERANCE); // Does not restore to the exact original value because scale up is a bit offset. assertEquals(0.4f, initial.scale(0.8f).getScale(), TOLERANCE); assertEquals(0.48f, initial.scale(0.8f).scale(1.25f).getScale(), TOLERANCE); } @Test public void testScale_zeroPrimitiveScaleValue() { PrimitiveSegment initial = new PrimitiveSegment( Loading