Loading core/java/android/os/Vibrator.java +22 −0 Original line number Diff line number Diff line Loading @@ -207,6 +207,28 @@ public abstract class Vibrator { */ public abstract boolean hasAmplitudeControl(); /** * Gets the resonant frequency of the vibrator. * * @return the resonant frequency of the vibrator, or {@link Float#NaN NaN} if it's unknown or * this vibrator is a composite of multiple physical devices. * @hide */ public float getResonantFrequency() { return Float.NaN; } /** * Gets the <a href="https://en.wikipedia.org/wiki/Q_factor">Q factor</a> of the vibrator. * * @return the Q factor of the vibrator, or {@link Float#NaN NaN} if it's unknown or * this vibrator is a composite of multiple physical devices. * @hide */ public float getQFactor() { return Float.NaN; } /** * Configure an always-on haptics effect. * Loading core/java/android/os/VibratorInfo.java +36 −3 Original line number Diff line number Diff line Loading @@ -42,21 +42,27 @@ public final class VibratorInfo implements Parcelable { private final SparseBooleanArray mSupportedEffects; @Nullable private final SparseBooleanArray mSupportedPrimitives; private final float mResonantFrequency; private final float mQFactor; VibratorInfo(Parcel in) { mId = in.readInt(); mCapabilities = in.readLong(); mSupportedEffects = in.readSparseBooleanArray(); mSupportedPrimitives = in.readSparseBooleanArray(); mResonantFrequency = in.readFloat(); mQFactor = in.readFloat(); } /** @hide */ public VibratorInfo(int id, long capabilities, int[] supportedEffects, int[] supportedPrimitives) { int[] supportedPrimitives, float resonantFrequency, float qFactor) { mId = id; mCapabilities = capabilities; mSupportedEffects = toSparseBooleanArray(supportedEffects); mSupportedPrimitives = toSparseBooleanArray(supportedPrimitives); mResonantFrequency = resonantFrequency; mQFactor = qFactor; } @Override Loading @@ -65,6 +71,8 @@ public final class VibratorInfo implements Parcelable { dest.writeLong(mCapabilities); dest.writeSparseBooleanArray(mSupportedEffects); dest.writeSparseBooleanArray(mSupportedPrimitives); dest.writeFloat(mResonantFrequency); dest.writeFloat(mQFactor); } @Override Loading @@ -83,12 +91,15 @@ public final class VibratorInfo implements Parcelable { VibratorInfo that = (VibratorInfo) o; return mId == that.mId && mCapabilities == that.mCapabilities && Objects.equals(mSupportedEffects, that.mSupportedEffects) && Objects.equals(mSupportedPrimitives, that.mSupportedPrimitives); && Objects.equals(mSupportedPrimitives, that.mSupportedPrimitives) && Objects.equals(mResonantFrequency, that.mResonantFrequency) && Objects.equals(mQFactor, that.mQFactor); } @Override public int hashCode() { return Objects.hash(mId, mCapabilities, mSupportedEffects, mSupportedPrimitives); return Objects.hash(mId, mCapabilities, mSupportedEffects, mSupportedPrimitives, mResonantFrequency, mQFactor); } @Override Loading @@ -99,6 +110,8 @@ public final class VibratorInfo implements Parcelable { + ", mCapabilities flags=" + Long.toBinaryString(mCapabilities) + ", mSupportedEffects=" + Arrays.toString(getSupportedEffectsNames()) + ", mSupportedPrimitives=" + Arrays.toString(getSupportedPrimitivesNames()) + ", mResonantFrequency=" + mResonantFrequency + ", mQFactor=" + mQFactor + '}'; } Loading Loading @@ -156,6 +169,26 @@ public final class VibratorInfo implements Parcelable { return (mCapabilities & capability) == capability; } /** * Gets the resonant frequency of the vibrator. * * @return the resonant frequency of the vibrator, or {@link Float#NaN NaN} if it's unknown or * this vibrator is a composite of multiple physical devices. */ public float getResonantFrequency() { return mResonantFrequency; } /** * Gets the <a href="https://en.wikipedia.org/wiki/Q_factor">Q factor</a> of the vibrator. * * @return the Q factor of the vibrator, or {@link Float#NaN NaN} if it's unknown or * this vibrator is a composite of multiple physical devices. */ public float getQFactor() { return mQFactor; } private String[] getCapabilitiesNames() { List<String> names = new ArrayList<>(); if (hasCapability(IVibrator.CAP_ON_CALLBACK)) { Loading core/tests/coretests/src/android/os/VibratorInfoTest.java +139 −42 Original line number Diff line number Diff line Loading @@ -16,9 +16,10 @@ package android.os; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; import android.hardware.vibrator.IVibrator; import android.platform.test.annotations.Presubmit; Loading @@ -33,72 +34,128 @@ public class VibratorInfoTest { @Test public void testHasAmplitudeControl() { assertFalse(createInfo(/* capabilities= */ 0).hasAmplitudeControl()); assertTrue(createInfo(IVibrator.CAP_COMPOSE_EFFECTS | IVibrator.CAP_AMPLITUDE_CONTROL).hasAmplitudeControl()); VibratorInfo noCapabilities = new InfoBuilder().build(); assertFalse(noCapabilities.hasAmplitudeControl()); VibratorInfo composeAndAmplitudeControl = new InfoBuilder() .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS | IVibrator.CAP_AMPLITUDE_CONTROL) .build(); assertTrue(composeAndAmplitudeControl.hasAmplitudeControl()); } @Test public void testHasCapabilities() { assertTrue(createInfo(IVibrator.CAP_COMPOSE_EFFECTS) .hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)); assertFalse(createInfo(IVibrator.CAP_COMPOSE_EFFECTS) .hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL)); VibratorInfo info = new InfoBuilder() .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) .build(); assertTrue(info.hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)); assertFalse(info.hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL)); } @Test public void testIsEffectSupported() { VibratorInfo info = new VibratorInfo(/* id= */ 0, /* capabilities= */0, new int[]{VibrationEffect.EFFECT_CLICK}, null); VibratorInfo noEffects = new InfoBuilder().build(); VibratorInfo canClick = new InfoBuilder() .setSupportedEffects(VibrationEffect.EFFECT_CLICK) .build(); assertEquals(Vibrator.VIBRATION_EFFECT_SUPPORT_UNKNOWN, createInfo(/* capabilities= */ 0).isEffectSupported(VibrationEffect.EFFECT_CLICK)); noEffects.isEffectSupported(VibrationEffect.EFFECT_CLICK)); assertEquals(Vibrator.VIBRATION_EFFECT_SUPPORT_YES, info.isEffectSupported(VibrationEffect.EFFECT_CLICK)); canClick.isEffectSupported(VibrationEffect.EFFECT_CLICK)); assertEquals(Vibrator.VIBRATION_EFFECT_SUPPORT_NO, info.isEffectSupported(VibrationEffect.EFFECT_TICK)); canClick.isEffectSupported(VibrationEffect.EFFECT_TICK)); } @Test public void testIsPrimitiveSupported() { VibratorInfo info = new VibratorInfo(/* id= */ 0, IVibrator.CAP_COMPOSE_EFFECTS, null, new int[]{VibrationEffect.Composition.PRIMITIVE_CLICK}); VibratorInfo info = new InfoBuilder() .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) .setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK) .build(); assertTrue(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_CLICK)); assertFalse(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_TICK)); // Returns false when there is no compose capability. info = new VibratorInfo(/* id= */ 0, /* capabilities= */ 0, null, new int[]{VibrationEffect.Composition.PRIMITIVE_CLICK}); info = new InfoBuilder() .setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK) .build(); assertFalse(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_CLICK)); } @Test public void testEquals() { VibratorInfo empty = new VibratorInfo(1, 0, null, null); VibratorInfo complete = new VibratorInfo(1, IVibrator.CAP_AMPLITUDE_CONTROL, new int[]{VibrationEffect.EFFECT_CLICK}, new int[]{VibrationEffect.Composition.PRIMITIVE_CLICK}); InfoBuilder completeBuilder = new InfoBuilder() .setId(1) .setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL) .setSupportedEffects(VibrationEffect.EFFECT_CLICK) .setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK) .setQFactor(2f) .setResonantFrequency(150f); VibratorInfo complete = completeBuilder.build(); assertEquals(complete, complete); assertEquals(complete, new VibratorInfo(1, IVibrator.CAP_AMPLITUDE_CONTROL, new int[]{VibrationEffect.EFFECT_CLICK}, new int[]{VibrationEffect.Composition.PRIMITIVE_CLICK})); assertFalse(empty.equals(new VibratorInfo(1, 0, new int[]{}, new int[]{}))); assertFalse(complete.equals(new VibratorInfo(1, IVibrator.CAP_COMPOSE_EFFECTS, new int[]{VibrationEffect.EFFECT_CLICK}, new int[]{VibrationEffect.Composition.PRIMITIVE_CLICK}))); assertFalse(complete.equals(new VibratorInfo(1, IVibrator.CAP_AMPLITUDE_CONTROL, new int[]{}, new int[]{}))); assertFalse(complete.equals(new VibratorInfo(1, IVibrator.CAP_AMPLITUDE_CONTROL, null, new int[]{VibrationEffect.Composition.PRIMITIVE_CLICK}))); assertFalse(complete.equals(new VibratorInfo(1, IVibrator.CAP_AMPLITUDE_CONTROL, new int[]{VibrationEffect.EFFECT_CLICK}, null))); assertEquals(complete, completeBuilder.build()); VibratorInfo completeWithComposeControl = completeBuilder .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) .build(); assertNotEquals(complete, completeWithComposeControl); VibratorInfo completeWithNoEffects = completeBuilder .setSupportedEffects() .setSupportedPrimitives() .build(); assertNotEquals(complete, completeWithNoEffects); VibratorInfo completeWithUnknownEffects = completeBuilder .setSupportedEffects(null) .build(); assertNotEquals(complete, completeWithNoEffects); VibratorInfo completeWithUnknownPrimitives = completeBuilder .setSupportedPrimitives(null) .build(); assertNotEquals(complete, completeWithUnknownPrimitives); VibratorInfo completeWithDifferentF0 = completeBuilder .setResonantFrequency(complete.getResonantFrequency() + 3f) .build(); assertNotEquals(complete, completeWithDifferentF0); VibratorInfo completeWithUnknownF0 = completeBuilder .setResonantFrequency(Float.NaN) .build(); assertNotEquals(complete, completeWithUnknownF0); VibratorInfo completeWithUnknownQFactor = completeBuilder .setQFactor(Float.NaN) .build(); assertNotEquals(complete, completeWithUnknownQFactor); VibratorInfo completeWithDifferentQFactor = completeBuilder .setQFactor(complete.getQFactor() + 3f) .build(); assertNotEquals(complete, completeWithDifferentQFactor); VibratorInfo empty = new InfoBuilder().setId(1).build(); VibratorInfo emptyWithKnownSupport = new InfoBuilder() .setId(1) .setSupportedEffects() .setSupportedPrimitives() .build(); assertNotEquals(empty, emptyWithKnownSupport); } @Test public void testSerialization() { VibratorInfo original = new VibratorInfo(1, IVibrator.CAP_COMPOSE_EFFECTS, new int[]{VibrationEffect.EFFECT_CLICK}, null); public void testParceling() { VibratorInfo original = new InfoBuilder() .setId(1) .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) .setSupportedEffects(VibrationEffect.EFFECT_CLICK) .setSupportedPrimitives(null) .setResonantFrequency(1.3f) .setQFactor(Float.NaN) .build(); Parcel parcel = Parcel.obtain(); original.writeToParcel(parcel, 0); Loading @@ -107,7 +164,47 @@ public class VibratorInfoTest { assertEquals(original, restored); } private static VibratorInfo createInfo(long capabilities) { return new VibratorInfo(/* id= */ 0, capabilities, null, null); private static class InfoBuilder { private int mId = 0; private int mCapabilities = 0; private int[] mSupportedEffects = null; private int[] mSupportedPrimitives = null; private float mResonantFrequency = Float.NaN; private float mQFactor = Float.NaN; public InfoBuilder setId(int id) { mId = id; return this; } public InfoBuilder setCapabilities(int capabilities) { mCapabilities = capabilities; return this; } public InfoBuilder setSupportedEffects(int... supportedEffects) { mSupportedEffects = supportedEffects; return this; } public InfoBuilder setSupportedPrimitives(int... supportedPrimitives) { mSupportedPrimitives = supportedPrimitives; return this; } public InfoBuilder setResonantFrequency(float resonantFrequency) { mResonantFrequency = resonantFrequency; return this; } public InfoBuilder setQFactor(float qFactor) { mQFactor = qFactor; return this; } public VibratorInfo build() { return new VibratorInfo(mId, mCapabilities, mSupportedEffects, mSupportedPrimitives, mResonantFrequency, mQFactor); } } } services/core/java/com/android/server/vibrator/VibratorController.java +60 −61 Original line number Diff line number Diff line Loading @@ -54,50 +54,6 @@ final class VibratorController { void onComplete(int vibratorId, long vibrationId); } /** * Initializes the native part of this controller, creating a global reference to given * {@link OnVibrationCompleteListener} and returns a newly allocated native pointer. This * wrapper is responsible for deleting this pointer by calling the method pointed * by {@link #vibratorGetFinalizer()}. * * <p><b>Note:</b> Make sure the given implementation of {@link OnVibrationCompleteListener} * do not hold any strong reference to the instance responsible for deleting the returned * pointer, to avoid creating a cyclic GC root reference. */ static native long vibratorInit(int vibratorId, OnVibrationCompleteListener listener); /** * Returns pointer to native function responsible for cleaning up the native pointer allocated * and returned by {@link #vibratorInit(int, OnVibrationCompleteListener)}. */ static native long vibratorGetFinalizer(); static native boolean vibratorIsAvailable(long nativePtr); static native void vibratorOn(long nativePtr, long milliseconds, long vibrationId); static native void vibratorOff(long nativePtr); static native void vibratorSetAmplitude(long nativePtr, int amplitude); static native int[] vibratorGetSupportedEffects(long nativePtr); static native int[] vibratorGetSupportedPrimitives(long nativePtr); static native long vibratorPerformEffect( long nativePtr, long effect, long strength, long vibrationId); static native long vibratorPerformComposedEffect( long nativePtr, VibrationEffect.Composition.PrimitiveEffect[] effect, long vibrationId); static native void vibratorSetExternalControl(long nativePtr, boolean enabled); static native long vibratorGetCapabilities(long nativePtr); static native void vibratorAlwaysOnEnable(long nativePtr, long id, long effect, long strength); static native void vibratorAlwaysOnDisable(long nativePtr, long id); VibratorController(int vibratorId, OnVibrationCompleteListener listener) { this(vibratorId, listener, new NativeWrapper()); } Loading @@ -109,7 +65,8 @@ final class VibratorController { mNativeWrapper.init(vibratorId, listener); mVibratorInfo = new VibratorInfo(vibratorId, nativeWrapper.getCapabilities(), nativeWrapper.getSupportedEffects(), nativeWrapper.getSupportedPrimitives()); nativeWrapper.getSupportedEffects(), nativeWrapper.getSupportedPrimitives(), nativeWrapper.getResonantFrequency(), nativeWrapper.getQFactor()); } /** Register state listener for this vibrator. */ Loading Loading @@ -336,13 +293,47 @@ final class VibratorController { /** Wrapper around the static-native methods of {@link VibratorController} for tests. */ @VisibleForTesting public static class NativeWrapper { /** * Initializes the native part of this controller, creating a global reference to given * {@link OnVibrationCompleteListener} and returns a newly allocated native pointer. This * wrapper is responsible for deleting this pointer by calling the method pointed * by {@link #getNativeFinalizer()}. * * <p><b>Note:</b> Make sure the given implementation of {@link OnVibrationCompleteListener} * do not hold any strong reference to the instance responsible for deleting the returned * pointer, to avoid creating a cyclic GC root reference. */ private static native long nativeInit(int vibratorId, OnVibrationCompleteListener listener); /** * Returns pointer to native function responsible for cleaning up the native pointer * allocated and returned by {@link #nativeInit(int, OnVibrationCompleteListener)}. */ private static native long getNativeFinalizer(); private static native boolean isAvailable(long nativePtr); private static native void on(long nativePtr, long milliseconds, long vibrationId); private static native void off(long nativePtr); private static native void setAmplitude(long nativePtr, int amplitude); private static native int[] getSupportedEffects(long nativePtr); private static native int[] getSupportedPrimitives(long nativePtr); private static native long performEffect( long nativePtr, long effect, long strength, long vibrationId); private static native long performComposedEffect(long nativePtr, VibrationEffect.Composition.PrimitiveEffect[] effect, long vibrationId); private static native void setExternalControl(long nativePtr, boolean enabled); private static native long getCapabilities(long nativePtr); private static native void alwaysOnEnable(long nativePtr, long id, long effect, long strength); private static native void alwaysOnDisable(long nativePtr, long id); private static native float getResonantFrequency(long nativePtr); private static native float getQFactor(long nativePtr); private long mNativePtr = 0; /** Initializes native controller and allocation registry to destroy native instances. */ public void init(int vibratorId, OnVibrationCompleteListener listener) { mNativePtr = VibratorController.vibratorInit(vibratorId, listener); long finalizerPtr = VibratorController.vibratorGetFinalizer(); mNativePtr = nativeInit(vibratorId, listener); long finalizerPtr = getNativeFinalizer(); if (finalizerPtr != 0) { NativeAllocationRegistry registry = Loading @@ -354,65 +345,73 @@ final class VibratorController { /** Check if the vibrator is currently available. */ public boolean isAvailable() { return VibratorController.vibratorIsAvailable(mNativePtr); return isAvailable(mNativePtr); } /** Turns vibrator on for given time. */ public void on(long milliseconds, long vibrationId) { VibratorController.vibratorOn(mNativePtr, milliseconds, vibrationId); on(mNativePtr, milliseconds, vibrationId); } /** Turns vibrator off. */ public void off() { VibratorController.vibratorOff(mNativePtr); off(mNativePtr); } /** Sets the amplitude for the vibrator to run. */ public void setAmplitude(int amplitude) { VibratorController.vibratorSetAmplitude(mNativePtr, amplitude); setAmplitude(mNativePtr, amplitude); } /** Returns all predefined effects supported by the device vibrator. */ public int[] getSupportedEffects() { return VibratorController.vibratorGetSupportedEffects(mNativePtr); return getSupportedEffects(mNativePtr); } /** Returns all compose primitives supported by the device vibrator. */ public int[] getSupportedPrimitives() { return VibratorController.vibratorGetSupportedPrimitives(mNativePtr); return getSupportedPrimitives(mNativePtr); } /** Turns vibrator on to perform one of the supported effects. */ public long perform(long effect, long strength, long vibrationId) { return VibratorController.vibratorPerformEffect( mNativePtr, effect, strength, vibrationId); return performEffect(mNativePtr, effect, strength, vibrationId); } /** Turns vibrator on to perform one of the supported composed effects. */ public long compose( VibrationEffect.Composition.PrimitiveEffect[] effect, long vibrationId) { return VibratorController.vibratorPerformComposedEffect(mNativePtr, effect, vibrationId); return performComposedEffect(mNativePtr, effect, vibrationId); } /** Enabled the device vibrator to be controlled by another service. */ public void setExternalControl(boolean enabled) { VibratorController.vibratorSetExternalControl(mNativePtr, enabled); setExternalControl(mNativePtr, enabled); } /** Returns all capabilities of the device vibrator. */ public long getCapabilities() { return VibratorController.vibratorGetCapabilities(mNativePtr); return getCapabilities(mNativePtr); } /** Enable always-on vibration with given id and effect. */ public void alwaysOnEnable(long id, long effect, long strength) { VibratorController.vibratorAlwaysOnEnable(mNativePtr, id, effect, strength); alwaysOnEnable(mNativePtr, id, effect, strength); } /** Disable always-on vibration for given id. */ public void alwaysOnDisable(long id) { VibratorController.vibratorAlwaysOnDisable(mNativePtr, id); alwaysOnDisable(mNativePtr, id); } /** Gets the vibrator's resonant frequency (F0) */ public float getResonantFrequency() { return getResonantFrequency(mNativePtr); } /** Gets the vibrator's Q factor */ public float getQFactor() { return getQFactor(mNativePtr); } } } services/core/jni/com_android_server_vibrator_VibratorController.cpp +45 −24 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
core/java/android/os/Vibrator.java +22 −0 Original line number Diff line number Diff line Loading @@ -207,6 +207,28 @@ public abstract class Vibrator { */ public abstract boolean hasAmplitudeControl(); /** * Gets the resonant frequency of the vibrator. * * @return the resonant frequency of the vibrator, or {@link Float#NaN NaN} if it's unknown or * this vibrator is a composite of multiple physical devices. * @hide */ public float getResonantFrequency() { return Float.NaN; } /** * Gets the <a href="https://en.wikipedia.org/wiki/Q_factor">Q factor</a> of the vibrator. * * @return the Q factor of the vibrator, or {@link Float#NaN NaN} if it's unknown or * this vibrator is a composite of multiple physical devices. * @hide */ public float getQFactor() { return Float.NaN; } /** * Configure an always-on haptics effect. * Loading
core/java/android/os/VibratorInfo.java +36 −3 Original line number Diff line number Diff line Loading @@ -42,21 +42,27 @@ public final class VibratorInfo implements Parcelable { private final SparseBooleanArray mSupportedEffects; @Nullable private final SparseBooleanArray mSupportedPrimitives; private final float mResonantFrequency; private final float mQFactor; VibratorInfo(Parcel in) { mId = in.readInt(); mCapabilities = in.readLong(); mSupportedEffects = in.readSparseBooleanArray(); mSupportedPrimitives = in.readSparseBooleanArray(); mResonantFrequency = in.readFloat(); mQFactor = in.readFloat(); } /** @hide */ public VibratorInfo(int id, long capabilities, int[] supportedEffects, int[] supportedPrimitives) { int[] supportedPrimitives, float resonantFrequency, float qFactor) { mId = id; mCapabilities = capabilities; mSupportedEffects = toSparseBooleanArray(supportedEffects); mSupportedPrimitives = toSparseBooleanArray(supportedPrimitives); mResonantFrequency = resonantFrequency; mQFactor = qFactor; } @Override Loading @@ -65,6 +71,8 @@ public final class VibratorInfo implements Parcelable { dest.writeLong(mCapabilities); dest.writeSparseBooleanArray(mSupportedEffects); dest.writeSparseBooleanArray(mSupportedPrimitives); dest.writeFloat(mResonantFrequency); dest.writeFloat(mQFactor); } @Override Loading @@ -83,12 +91,15 @@ public final class VibratorInfo implements Parcelable { VibratorInfo that = (VibratorInfo) o; return mId == that.mId && mCapabilities == that.mCapabilities && Objects.equals(mSupportedEffects, that.mSupportedEffects) && Objects.equals(mSupportedPrimitives, that.mSupportedPrimitives); && Objects.equals(mSupportedPrimitives, that.mSupportedPrimitives) && Objects.equals(mResonantFrequency, that.mResonantFrequency) && Objects.equals(mQFactor, that.mQFactor); } @Override public int hashCode() { return Objects.hash(mId, mCapabilities, mSupportedEffects, mSupportedPrimitives); return Objects.hash(mId, mCapabilities, mSupportedEffects, mSupportedPrimitives, mResonantFrequency, mQFactor); } @Override Loading @@ -99,6 +110,8 @@ public final class VibratorInfo implements Parcelable { + ", mCapabilities flags=" + Long.toBinaryString(mCapabilities) + ", mSupportedEffects=" + Arrays.toString(getSupportedEffectsNames()) + ", mSupportedPrimitives=" + Arrays.toString(getSupportedPrimitivesNames()) + ", mResonantFrequency=" + mResonantFrequency + ", mQFactor=" + mQFactor + '}'; } Loading Loading @@ -156,6 +169,26 @@ public final class VibratorInfo implements Parcelable { return (mCapabilities & capability) == capability; } /** * Gets the resonant frequency of the vibrator. * * @return the resonant frequency of the vibrator, or {@link Float#NaN NaN} if it's unknown or * this vibrator is a composite of multiple physical devices. */ public float getResonantFrequency() { return mResonantFrequency; } /** * Gets the <a href="https://en.wikipedia.org/wiki/Q_factor">Q factor</a> of the vibrator. * * @return the Q factor of the vibrator, or {@link Float#NaN NaN} if it's unknown or * this vibrator is a composite of multiple physical devices. */ public float getQFactor() { return mQFactor; } private String[] getCapabilitiesNames() { List<String> names = new ArrayList<>(); if (hasCapability(IVibrator.CAP_ON_CALLBACK)) { Loading
core/tests/coretests/src/android/os/VibratorInfoTest.java +139 −42 Original line number Diff line number Diff line Loading @@ -16,9 +16,10 @@ package android.os; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; import android.hardware.vibrator.IVibrator; import android.platform.test.annotations.Presubmit; Loading @@ -33,72 +34,128 @@ public class VibratorInfoTest { @Test public void testHasAmplitudeControl() { assertFalse(createInfo(/* capabilities= */ 0).hasAmplitudeControl()); assertTrue(createInfo(IVibrator.CAP_COMPOSE_EFFECTS | IVibrator.CAP_AMPLITUDE_CONTROL).hasAmplitudeControl()); VibratorInfo noCapabilities = new InfoBuilder().build(); assertFalse(noCapabilities.hasAmplitudeControl()); VibratorInfo composeAndAmplitudeControl = new InfoBuilder() .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS | IVibrator.CAP_AMPLITUDE_CONTROL) .build(); assertTrue(composeAndAmplitudeControl.hasAmplitudeControl()); } @Test public void testHasCapabilities() { assertTrue(createInfo(IVibrator.CAP_COMPOSE_EFFECTS) .hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)); assertFalse(createInfo(IVibrator.CAP_COMPOSE_EFFECTS) .hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL)); VibratorInfo info = new InfoBuilder() .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) .build(); assertTrue(info.hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)); assertFalse(info.hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL)); } @Test public void testIsEffectSupported() { VibratorInfo info = new VibratorInfo(/* id= */ 0, /* capabilities= */0, new int[]{VibrationEffect.EFFECT_CLICK}, null); VibratorInfo noEffects = new InfoBuilder().build(); VibratorInfo canClick = new InfoBuilder() .setSupportedEffects(VibrationEffect.EFFECT_CLICK) .build(); assertEquals(Vibrator.VIBRATION_EFFECT_SUPPORT_UNKNOWN, createInfo(/* capabilities= */ 0).isEffectSupported(VibrationEffect.EFFECT_CLICK)); noEffects.isEffectSupported(VibrationEffect.EFFECT_CLICK)); assertEquals(Vibrator.VIBRATION_EFFECT_SUPPORT_YES, info.isEffectSupported(VibrationEffect.EFFECT_CLICK)); canClick.isEffectSupported(VibrationEffect.EFFECT_CLICK)); assertEquals(Vibrator.VIBRATION_EFFECT_SUPPORT_NO, info.isEffectSupported(VibrationEffect.EFFECT_TICK)); canClick.isEffectSupported(VibrationEffect.EFFECT_TICK)); } @Test public void testIsPrimitiveSupported() { VibratorInfo info = new VibratorInfo(/* id= */ 0, IVibrator.CAP_COMPOSE_EFFECTS, null, new int[]{VibrationEffect.Composition.PRIMITIVE_CLICK}); VibratorInfo info = new InfoBuilder() .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) .setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK) .build(); assertTrue(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_CLICK)); assertFalse(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_TICK)); // Returns false when there is no compose capability. info = new VibratorInfo(/* id= */ 0, /* capabilities= */ 0, null, new int[]{VibrationEffect.Composition.PRIMITIVE_CLICK}); info = new InfoBuilder() .setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK) .build(); assertFalse(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_CLICK)); } @Test public void testEquals() { VibratorInfo empty = new VibratorInfo(1, 0, null, null); VibratorInfo complete = new VibratorInfo(1, IVibrator.CAP_AMPLITUDE_CONTROL, new int[]{VibrationEffect.EFFECT_CLICK}, new int[]{VibrationEffect.Composition.PRIMITIVE_CLICK}); InfoBuilder completeBuilder = new InfoBuilder() .setId(1) .setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL) .setSupportedEffects(VibrationEffect.EFFECT_CLICK) .setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK) .setQFactor(2f) .setResonantFrequency(150f); VibratorInfo complete = completeBuilder.build(); assertEquals(complete, complete); assertEquals(complete, new VibratorInfo(1, IVibrator.CAP_AMPLITUDE_CONTROL, new int[]{VibrationEffect.EFFECT_CLICK}, new int[]{VibrationEffect.Composition.PRIMITIVE_CLICK})); assertFalse(empty.equals(new VibratorInfo(1, 0, new int[]{}, new int[]{}))); assertFalse(complete.equals(new VibratorInfo(1, IVibrator.CAP_COMPOSE_EFFECTS, new int[]{VibrationEffect.EFFECT_CLICK}, new int[]{VibrationEffect.Composition.PRIMITIVE_CLICK}))); assertFalse(complete.equals(new VibratorInfo(1, IVibrator.CAP_AMPLITUDE_CONTROL, new int[]{}, new int[]{}))); assertFalse(complete.equals(new VibratorInfo(1, IVibrator.CAP_AMPLITUDE_CONTROL, null, new int[]{VibrationEffect.Composition.PRIMITIVE_CLICK}))); assertFalse(complete.equals(new VibratorInfo(1, IVibrator.CAP_AMPLITUDE_CONTROL, new int[]{VibrationEffect.EFFECT_CLICK}, null))); assertEquals(complete, completeBuilder.build()); VibratorInfo completeWithComposeControl = completeBuilder .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) .build(); assertNotEquals(complete, completeWithComposeControl); VibratorInfo completeWithNoEffects = completeBuilder .setSupportedEffects() .setSupportedPrimitives() .build(); assertNotEquals(complete, completeWithNoEffects); VibratorInfo completeWithUnknownEffects = completeBuilder .setSupportedEffects(null) .build(); assertNotEquals(complete, completeWithNoEffects); VibratorInfo completeWithUnknownPrimitives = completeBuilder .setSupportedPrimitives(null) .build(); assertNotEquals(complete, completeWithUnknownPrimitives); VibratorInfo completeWithDifferentF0 = completeBuilder .setResonantFrequency(complete.getResonantFrequency() + 3f) .build(); assertNotEquals(complete, completeWithDifferentF0); VibratorInfo completeWithUnknownF0 = completeBuilder .setResonantFrequency(Float.NaN) .build(); assertNotEquals(complete, completeWithUnknownF0); VibratorInfo completeWithUnknownQFactor = completeBuilder .setQFactor(Float.NaN) .build(); assertNotEquals(complete, completeWithUnknownQFactor); VibratorInfo completeWithDifferentQFactor = completeBuilder .setQFactor(complete.getQFactor() + 3f) .build(); assertNotEquals(complete, completeWithDifferentQFactor); VibratorInfo empty = new InfoBuilder().setId(1).build(); VibratorInfo emptyWithKnownSupport = new InfoBuilder() .setId(1) .setSupportedEffects() .setSupportedPrimitives() .build(); assertNotEquals(empty, emptyWithKnownSupport); } @Test public void testSerialization() { VibratorInfo original = new VibratorInfo(1, IVibrator.CAP_COMPOSE_EFFECTS, new int[]{VibrationEffect.EFFECT_CLICK}, null); public void testParceling() { VibratorInfo original = new InfoBuilder() .setId(1) .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) .setSupportedEffects(VibrationEffect.EFFECT_CLICK) .setSupportedPrimitives(null) .setResonantFrequency(1.3f) .setQFactor(Float.NaN) .build(); Parcel parcel = Parcel.obtain(); original.writeToParcel(parcel, 0); Loading @@ -107,7 +164,47 @@ public class VibratorInfoTest { assertEquals(original, restored); } private static VibratorInfo createInfo(long capabilities) { return new VibratorInfo(/* id= */ 0, capabilities, null, null); private static class InfoBuilder { private int mId = 0; private int mCapabilities = 0; private int[] mSupportedEffects = null; private int[] mSupportedPrimitives = null; private float mResonantFrequency = Float.NaN; private float mQFactor = Float.NaN; public InfoBuilder setId(int id) { mId = id; return this; } public InfoBuilder setCapabilities(int capabilities) { mCapabilities = capabilities; return this; } public InfoBuilder setSupportedEffects(int... supportedEffects) { mSupportedEffects = supportedEffects; return this; } public InfoBuilder setSupportedPrimitives(int... supportedPrimitives) { mSupportedPrimitives = supportedPrimitives; return this; } public InfoBuilder setResonantFrequency(float resonantFrequency) { mResonantFrequency = resonantFrequency; return this; } public InfoBuilder setQFactor(float qFactor) { mQFactor = qFactor; return this; } public VibratorInfo build() { return new VibratorInfo(mId, mCapabilities, mSupportedEffects, mSupportedPrimitives, mResonantFrequency, mQFactor); } } }
services/core/java/com/android/server/vibrator/VibratorController.java +60 −61 Original line number Diff line number Diff line Loading @@ -54,50 +54,6 @@ final class VibratorController { void onComplete(int vibratorId, long vibrationId); } /** * Initializes the native part of this controller, creating a global reference to given * {@link OnVibrationCompleteListener} and returns a newly allocated native pointer. This * wrapper is responsible for deleting this pointer by calling the method pointed * by {@link #vibratorGetFinalizer()}. * * <p><b>Note:</b> Make sure the given implementation of {@link OnVibrationCompleteListener} * do not hold any strong reference to the instance responsible for deleting the returned * pointer, to avoid creating a cyclic GC root reference. */ static native long vibratorInit(int vibratorId, OnVibrationCompleteListener listener); /** * Returns pointer to native function responsible for cleaning up the native pointer allocated * and returned by {@link #vibratorInit(int, OnVibrationCompleteListener)}. */ static native long vibratorGetFinalizer(); static native boolean vibratorIsAvailable(long nativePtr); static native void vibratorOn(long nativePtr, long milliseconds, long vibrationId); static native void vibratorOff(long nativePtr); static native void vibratorSetAmplitude(long nativePtr, int amplitude); static native int[] vibratorGetSupportedEffects(long nativePtr); static native int[] vibratorGetSupportedPrimitives(long nativePtr); static native long vibratorPerformEffect( long nativePtr, long effect, long strength, long vibrationId); static native long vibratorPerformComposedEffect( long nativePtr, VibrationEffect.Composition.PrimitiveEffect[] effect, long vibrationId); static native void vibratorSetExternalControl(long nativePtr, boolean enabled); static native long vibratorGetCapabilities(long nativePtr); static native void vibratorAlwaysOnEnable(long nativePtr, long id, long effect, long strength); static native void vibratorAlwaysOnDisable(long nativePtr, long id); VibratorController(int vibratorId, OnVibrationCompleteListener listener) { this(vibratorId, listener, new NativeWrapper()); } Loading @@ -109,7 +65,8 @@ final class VibratorController { mNativeWrapper.init(vibratorId, listener); mVibratorInfo = new VibratorInfo(vibratorId, nativeWrapper.getCapabilities(), nativeWrapper.getSupportedEffects(), nativeWrapper.getSupportedPrimitives()); nativeWrapper.getSupportedEffects(), nativeWrapper.getSupportedPrimitives(), nativeWrapper.getResonantFrequency(), nativeWrapper.getQFactor()); } /** Register state listener for this vibrator. */ Loading Loading @@ -336,13 +293,47 @@ final class VibratorController { /** Wrapper around the static-native methods of {@link VibratorController} for tests. */ @VisibleForTesting public static class NativeWrapper { /** * Initializes the native part of this controller, creating a global reference to given * {@link OnVibrationCompleteListener} and returns a newly allocated native pointer. This * wrapper is responsible for deleting this pointer by calling the method pointed * by {@link #getNativeFinalizer()}. * * <p><b>Note:</b> Make sure the given implementation of {@link OnVibrationCompleteListener} * do not hold any strong reference to the instance responsible for deleting the returned * pointer, to avoid creating a cyclic GC root reference. */ private static native long nativeInit(int vibratorId, OnVibrationCompleteListener listener); /** * Returns pointer to native function responsible for cleaning up the native pointer * allocated and returned by {@link #nativeInit(int, OnVibrationCompleteListener)}. */ private static native long getNativeFinalizer(); private static native boolean isAvailable(long nativePtr); private static native void on(long nativePtr, long milliseconds, long vibrationId); private static native void off(long nativePtr); private static native void setAmplitude(long nativePtr, int amplitude); private static native int[] getSupportedEffects(long nativePtr); private static native int[] getSupportedPrimitives(long nativePtr); private static native long performEffect( long nativePtr, long effect, long strength, long vibrationId); private static native long performComposedEffect(long nativePtr, VibrationEffect.Composition.PrimitiveEffect[] effect, long vibrationId); private static native void setExternalControl(long nativePtr, boolean enabled); private static native long getCapabilities(long nativePtr); private static native void alwaysOnEnable(long nativePtr, long id, long effect, long strength); private static native void alwaysOnDisable(long nativePtr, long id); private static native float getResonantFrequency(long nativePtr); private static native float getQFactor(long nativePtr); private long mNativePtr = 0; /** Initializes native controller and allocation registry to destroy native instances. */ public void init(int vibratorId, OnVibrationCompleteListener listener) { mNativePtr = VibratorController.vibratorInit(vibratorId, listener); long finalizerPtr = VibratorController.vibratorGetFinalizer(); mNativePtr = nativeInit(vibratorId, listener); long finalizerPtr = getNativeFinalizer(); if (finalizerPtr != 0) { NativeAllocationRegistry registry = Loading @@ -354,65 +345,73 @@ final class VibratorController { /** Check if the vibrator is currently available. */ public boolean isAvailable() { return VibratorController.vibratorIsAvailable(mNativePtr); return isAvailable(mNativePtr); } /** Turns vibrator on for given time. */ public void on(long milliseconds, long vibrationId) { VibratorController.vibratorOn(mNativePtr, milliseconds, vibrationId); on(mNativePtr, milliseconds, vibrationId); } /** Turns vibrator off. */ public void off() { VibratorController.vibratorOff(mNativePtr); off(mNativePtr); } /** Sets the amplitude for the vibrator to run. */ public void setAmplitude(int amplitude) { VibratorController.vibratorSetAmplitude(mNativePtr, amplitude); setAmplitude(mNativePtr, amplitude); } /** Returns all predefined effects supported by the device vibrator. */ public int[] getSupportedEffects() { return VibratorController.vibratorGetSupportedEffects(mNativePtr); return getSupportedEffects(mNativePtr); } /** Returns all compose primitives supported by the device vibrator. */ public int[] getSupportedPrimitives() { return VibratorController.vibratorGetSupportedPrimitives(mNativePtr); return getSupportedPrimitives(mNativePtr); } /** Turns vibrator on to perform one of the supported effects. */ public long perform(long effect, long strength, long vibrationId) { return VibratorController.vibratorPerformEffect( mNativePtr, effect, strength, vibrationId); return performEffect(mNativePtr, effect, strength, vibrationId); } /** Turns vibrator on to perform one of the supported composed effects. */ public long compose( VibrationEffect.Composition.PrimitiveEffect[] effect, long vibrationId) { return VibratorController.vibratorPerformComposedEffect(mNativePtr, effect, vibrationId); return performComposedEffect(mNativePtr, effect, vibrationId); } /** Enabled the device vibrator to be controlled by another service. */ public void setExternalControl(boolean enabled) { VibratorController.vibratorSetExternalControl(mNativePtr, enabled); setExternalControl(mNativePtr, enabled); } /** Returns all capabilities of the device vibrator. */ public long getCapabilities() { return VibratorController.vibratorGetCapabilities(mNativePtr); return getCapabilities(mNativePtr); } /** Enable always-on vibration with given id and effect. */ public void alwaysOnEnable(long id, long effect, long strength) { VibratorController.vibratorAlwaysOnEnable(mNativePtr, id, effect, strength); alwaysOnEnable(mNativePtr, id, effect, strength); } /** Disable always-on vibration for given id. */ public void alwaysOnDisable(long id) { VibratorController.vibratorAlwaysOnDisable(mNativePtr, id); alwaysOnDisable(mNativePtr, id); } /** Gets the vibrator's resonant frequency (F0) */ public float getResonantFrequency() { return getResonantFrequency(mNativePtr); } /** Gets the vibrator's Q factor */ public float getQFactor() { return getQFactor(mNativePtr); } } }
services/core/jni/com_android_server_vibrator_VibratorController.cpp +45 −24 File changed.Preview size limit exceeded, changes collapsed. Show changes