Loading core/java/android/hardware/input/InputDeviceVibrator.java +13 −5 Original line number Diff line number Diff line Loading @@ -20,11 +20,13 @@ import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.app.ActivityThread; import android.content.Context; import android.hardware.vibrator.IVibrator; import android.os.Binder; import android.os.IVibratorStateListener; import android.os.VibrationAttributes; import android.os.VibrationEffect; import android.os.Vibrator; import android.os.VibratorInfo; import android.util.ArrayMap; import android.util.Log; Loading @@ -41,7 +43,7 @@ final class InputDeviceVibrator extends Vibrator { // mDeviceId represents InputDevice ID the vibrator belongs to private final int mDeviceId; private final int mVibratorId; private final VibratorInfo mVibratorInfo; private final Binder mToken; private final InputManager mInputManager; Loading @@ -52,7 +54,13 @@ final class InputDeviceVibrator extends Vibrator { InputDeviceVibrator(InputManager inputManager, int deviceId, int vibratorId) { mInputManager = inputManager; mDeviceId = deviceId; mVibratorId = vibratorId; mVibratorInfo = new VibratorInfo.Builder(vibratorId) .setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL) // Set predefined support to empty as we know input devices do not support them. .setSupportedEffects() .setSupportedPrimitives() .setSupportedBraking() .build(); mToken = new Binder(); } Loading @@ -74,8 +82,8 @@ final class InputDeviceVibrator extends Vibrator { } @Override public int getId() { return mVibratorId; protected VibratorInfo getInfo() { return mVibratorInfo; } @Override Loading Loading @@ -159,7 +167,7 @@ final class InputDeviceVibrator extends Vibrator { @Override public boolean hasAmplitudeControl() { return true; return mVibratorInfo.hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL); } /** Loading core/java/android/os/SystemVibrator.java +75 −79 Original line number Diff line number Diff line Loading @@ -28,7 +28,6 @@ import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; import java.util.ArrayList; import java.util.Arrays; import java.util.Objects; import java.util.concurrent.Executor; Loading @@ -50,6 +49,9 @@ public class SystemVibrator extends Vibrator { private final ArrayMap<OnVibratorStateChangedListener, AllVibratorsStateListener> mRegisteredListeners = new ArrayMap<>(); private final Object mLock = new Object(); private AllVibratorsInfo mVibratorInfo; @UnsupportedAppUsage public SystemVibrator(Context context) { super(context); Loading @@ -57,6 +59,25 @@ public class SystemVibrator extends Vibrator { mVibratorManager = mContext.getSystemService(VibratorManager.class); } @Override protected VibratorInfo getInfo() { synchronized (mLock) { if (mVibratorInfo != null) { return mVibratorInfo; } if (mVibratorManager == null) { Log.w(TAG, "Failed to retrieve vibrator info; no vibrator manager."); return VibratorInfo.EMPTY_VIBRATOR_INFO; } int[] vibratorIds = mVibratorManager.getVibratorIds(); VibratorInfo[] vibratorInfos = new VibratorInfo[vibratorIds.length]; for (int i = 0; i < vibratorIds.length; i++) { vibratorInfos[i] = mVibratorManager.getVibrator(vibratorIds[i]).getInfo(); } return mVibratorInfo = new AllVibratorsInfo(vibratorInfos); } } @Override public boolean hasVibrator() { if (mVibratorManager == null) { Loading Loading @@ -144,20 +165,7 @@ public class SystemVibrator extends Vibrator { @Override public boolean hasAmplitudeControl() { if (mVibratorManager == null) { Log.w(TAG, "Failed to check vibrator has amplitude control; no vibrator manager."); return false; } int[] vibratorIds = mVibratorManager.getVibratorIds(); if (vibratorIds.length == 0) { return false; } for (int vibratorId : vibratorIds) { if (!mVibratorManager.getVibrator(vibratorId).hasAmplitudeControl()) { return false; } } return true; return getInfo().hasAmplitudeControl(); } @Override Loading @@ -183,70 +191,6 @@ public class SystemVibrator extends Vibrator { mVibratorManager.vibrate(uid, opPkg, combinedEffect, reason, attributes); } @Override public int[] areEffectsSupported(@VibrationEffect.EffectType int... effectIds) { int[] supported = new int[effectIds.length]; if (mVibratorManager == null) { Log.w(TAG, "Failed to check supported effects; no vibrator manager."); Arrays.fill(supported, Vibrator.VIBRATION_EFFECT_SUPPORT_NO); return supported; } int[] vibratorIds = mVibratorManager.getVibratorIds(); if (vibratorIds.length == 0) { Arrays.fill(supported, Vibrator.VIBRATION_EFFECT_SUPPORT_NO); return supported; } int[][] vibratorSupportMap = new int[vibratorIds.length][effectIds.length]; for (int i = 0; i < vibratorIds.length; i++) { vibratorSupportMap[i] = mVibratorManager.getVibrator( vibratorIds[i]).areEffectsSupported(effectIds); } Arrays.fill(supported, Vibrator.VIBRATION_EFFECT_SUPPORT_YES); for (int effectIdx = 0; effectIdx < effectIds.length; effectIdx++) { for (int vibratorIdx = 0; vibratorIdx < vibratorIds.length; vibratorIdx++) { int effectSupported = vibratorSupportMap[vibratorIdx][effectIdx]; if (effectSupported == Vibrator.VIBRATION_EFFECT_SUPPORT_NO) { supported[effectIdx] = Vibrator.VIBRATION_EFFECT_SUPPORT_NO; break; } else if (effectSupported == Vibrator.VIBRATION_EFFECT_SUPPORT_UNKNOWN) { supported[effectIdx] = Vibrator.VIBRATION_EFFECT_SUPPORT_UNKNOWN; } } } return supported; } @Override public boolean[] arePrimitivesSupported( @NonNull @VibrationEffect.Composition.PrimitiveType int... primitiveIds) { boolean[] supported = new boolean[primitiveIds.length]; if (mVibratorManager == null) { Log.w(TAG, "Failed to check supported primitives; no vibrator manager."); Arrays.fill(supported, false); return supported; } int[] vibratorIds = mVibratorManager.getVibratorIds(); if (vibratorIds.length == 0) { Arrays.fill(supported, false); return supported; } boolean[][] vibratorSupportMap = new boolean[vibratorIds.length][primitiveIds.length]; for (int i = 0; i < vibratorIds.length; i++) { vibratorSupportMap[i] = mVibratorManager.getVibrator( vibratorIds[i]).arePrimitivesSupported(primitiveIds); } Arrays.fill(supported, true); for (int primitiveIdx = 0; primitiveIdx < primitiveIds.length; primitiveIdx++) { for (int vibratorIdx = 0; vibratorIdx < vibratorIds.length; vibratorIdx++) { if (!vibratorSupportMap[vibratorIdx][primitiveIdx]) { supported[primitiveIdx] = false; break; } } } return supported; } @Override public void cancel() { if (mVibratorManager == null) { Loading Loading @@ -304,6 +248,58 @@ public class SystemVibrator extends Vibrator { } } /** * Represents all the vibrators information as a single {@link VibratorInfo}. * * <p>This uses the first vibrator on the list as the default one for all hardware spec, but * uses an intersection of all vibrators to decide the capabilities and effect/primitive * support. */ private static class AllVibratorsInfo extends VibratorInfo { private final VibratorInfo[] mVibratorInfos; AllVibratorsInfo(VibratorInfo[] vibrators) { super(/* id= */ -1, capabilitiesIntersection(vibrators), vibrators.length > 0 ? vibrators[0] : VibratorInfo.EMPTY_VIBRATOR_INFO); mVibratorInfos = vibrators; } @Override public int isEffectSupported(int effectId) { int supported = Vibrator.VIBRATION_EFFECT_SUPPORT_YES; for (VibratorInfo info : mVibratorInfos) { int effectSupported = info.isEffectSupported(effectId); if (effectSupported == Vibrator.VIBRATION_EFFECT_SUPPORT_NO) { return effectSupported; } else if (effectSupported == Vibrator.VIBRATION_EFFECT_SUPPORT_UNKNOWN) { supported = effectSupported; } } return supported; } @Override public boolean isPrimitiveSupported(int primitiveId) { for (VibratorInfo info : mVibratorInfos) { if (!info.isPrimitiveSupported(primitiveId)) { return false; } } return true; } private static int capabilitiesIntersection(VibratorInfo[] infos) { if (infos.length == 0) { return 0; } int intersection = ~0; for (VibratorInfo info : infos) { intersection &= info.getCapabilities(); } return intersection; } } /** Listener for all vibrators state change. */ private static class AllVibratorsStateListener { private final Object mLock = new Object(); Loading core/java/android/os/SystemVibratorManager.java +2 −32 Original line number Diff line number Diff line Loading @@ -194,8 +194,8 @@ public class SystemVibratorManager extends VibratorManager { } @Override public int getId() { return mVibratorInfo.getId(); protected VibratorInfo getInfo() { return mVibratorInfo; } @Override Loading @@ -208,36 +208,6 @@ public class SystemVibratorManager extends VibratorManager { return mVibratorInfo.hasAmplitudeControl(); } @Override public float getResonantFrequency() { return mVibratorInfo.getResonantFrequency(); } @Override public float getQFactor() { return mVibratorInfo.getQFactor(); } @NonNull @Override public int[] areEffectsSupported(@NonNull int... effectIds) { int[] supported = new int[effectIds.length]; for (int i = 0; i < effectIds.length; i++) { supported[i] = mVibratorInfo.isEffectSupported(effectIds[i]); } return supported; } @Override public boolean[] arePrimitivesSupported( @NonNull @VibrationEffect.Composition.PrimitiveType int... primitiveIds) { boolean[] supported = new boolean[primitiveIds.length]; for (int i = 0; i < primitiveIds.length; i++) { supported[i] = mVibratorInfo.isPrimitiveSupported(primitiveIds[i]); } return supported; } @Override public boolean setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId, @Nullable VibrationEffect effect, @Nullable AudioAttributes attributes) { Loading core/java/android/os/Vibrator.java +73 −9 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package android.os; import android.annotation.CallbackExecutor; import android.annotation.FloatRange; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; Loading @@ -26,12 +27,13 @@ import android.annotation.SystemService; import android.app.ActivityThread; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.hardware.vibrator.IVibrator; import android.media.AudioAttributes; import android.util.Log; import android.util.Range; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Arrays; import java.util.concurrent.Executor; /** Loading Loading @@ -156,6 +158,11 @@ public abstract class Vibrator { return ctx != null ? ctx.getResources().getInteger(resId) : VIBRATION_INTENSITY_MEDIUM; } /** @hide */ protected VibratorInfo getInfo() { return VibratorInfo.EMPTY_VIBRATOR_INFO; } /** * Get the default vibration intensity for haptic feedback. * Loading Loading @@ -190,7 +197,7 @@ public abstract class Vibrator { * service, or -1 this service is not attached to any physical vibrator. */ public int getId() { return -1; return getInfo().getId(); } /** Loading @@ -207,6 +214,18 @@ public abstract class Vibrator { */ public abstract boolean hasAmplitudeControl(); /** * Check whether the vibrator has independent frequency control. * * @return True if the hardware can control the frequency of the vibrations, otherwise false. * @hide */ public boolean hasFrequencyControl() { // We currently can only control frequency of the vibration using the compose PWLE method. return getInfo().hasCapability( IVibrator.CAP_FREQUENCY_CONTROL | IVibrator.CAP_COMPOSE_PWLE_EFFECTS); } /** * Gets the resonant frequency of the vibrator. * Loading @@ -215,7 +234,7 @@ public abstract class Vibrator { * @hide */ public float getResonantFrequency() { return Float.NaN; return getInfo().getResonantFrequency(); } /** Loading @@ -226,7 +245,44 @@ public abstract class Vibrator { * @hide */ public float getQFactor() { return Float.NaN; return getInfo().getQFactor(); } /** * Return a range of relative frequency values supported by the vibrator. * * <p>These values can be used to create waveforms that controls the vibration frequency via * {@link VibrationEffect.WaveformBuilder}. * * @return A range of relative frequency values supported. The range will always contain the * value 0, representing the device resonant frequency. Devices without frequency control will * return the range [0,0]. Devices with frequency control will always return a range containing * the safe range [-1, 1]. * @hide */ public Range<Float> getRelativeFrequencyRange() { return getInfo().getFrequencyRange(); } /** * Return the maximum amplitude the vibrator can play at given relative frequency. * * <p>Devices without frequency control will return 1 for the input zero (resonant frequency), * and 0 to any other input. * * <p>Devices with frequency control will return the supported value, for input in * {@link #getRelativeFrequencyRange()}, and 0 for any other input. * * <p>These values can be used to create waveforms that plays vibrations outside the resonant * frequency via {@link VibrationEffect.WaveformBuilder}. * * @return a value in [0,1] representing the maximum amplitude the device can play at given * relative frequency. * @hide */ @FloatRange(from = 0, to = 1) public float getMaximumAmplitude(float relativeFrequency) { return getInfo().getMaxAmplitude(relativeFrequency); } /** Loading Loading @@ -414,9 +470,12 @@ public abstract class Vibrator { @VibrationEffectSupport public int[] areEffectsSupported( @NonNull @VibrationEffect.EffectType int... effectIds) { final int[] support = new int[effectIds.length]; Arrays.fill(support, VIBRATION_EFFECT_SUPPORT_NO); return support; VibratorInfo info = getInfo(); int[] supported = new int[effectIds.length]; for (int i = 0; i < effectIds.length; i++) { supported[i] = info.isEffectSupported(effectIds[i]); } return supported; } /** Loading Loading @@ -468,7 +527,12 @@ public abstract class Vibrator { @NonNull public boolean[] arePrimitivesSupported( @NonNull @VibrationEffect.Composition.PrimitiveType int... primitiveIds) { return new boolean[primitiveIds.length]; VibratorInfo info = getInfo(); boolean[] supported = new boolean[primitiveIds.length]; for (int i = 0; i < primitiveIds.length; i++) { supported[i] = info.isPrimitiveSupported(primitiveIds[i]); } return supported; } /** Loading core/java/android/os/VibratorInfo.java +96 −2 Original line number Diff line number Diff line Loading @@ -38,9 +38,12 @@ import java.util.Objects; * * @hide */ public final class VibratorInfo implements Parcelable { public class VibratorInfo implements Parcelable { private static final String TAG = "VibratorInfo"; /** @hide */ public static final VibratorInfo EMPTY_VIBRATOR_INFO = new VibratorInfo.Builder(-1).build(); private final int mId; private final long mCapabilities; @Nullable Loading Loading @@ -74,6 +77,23 @@ public final class VibratorInfo implements Parcelable { mFrequencyMapping = frequencyMapping; } protected VibratorInfo(int id, int capabilities, VibratorInfo baseVibrator) { mId = id; mCapabilities = capabilities; mSupportedEffects = baseVibrator.mSupportedEffects == null ? null : baseVibrator.mSupportedEffects.clone(); mSupportedBraking = baseVibrator.mSupportedBraking == null ? null : baseVibrator.mSupportedBraking.clone(); mSupportedPrimitives = baseVibrator.mSupportedPrimitives == null ? null : baseVibrator.mSupportedPrimitives.clone(); mQFactor = baseVibrator.mQFactor; mFrequencyMapping = new FrequencyMapping(baseVibrator.mFrequencyMapping.mMinFrequencyHz, baseVibrator.mFrequencyMapping.mResonantFrequencyHz, baseVibrator.mFrequencyMapping.mFrequencyResolutionHz, baseVibrator.mFrequencyMapping.mSuggestedSafeRangeHz, baseVibrator.mFrequencyMapping.mMaxAmplitudes); } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mId); Loading Loading @@ -145,6 +165,7 @@ public final class VibratorInfo implements Parcelable { * Returns a default value to be applied to composed PWLE effects for braking. * * @return a supported braking value, one of android.hardware.vibrator.Braking.* * @hide */ public int getDefaultBraking() { if (mSupportedBraking != null) { Loading Loading @@ -265,6 +286,10 @@ public final class VibratorInfo implements Parcelable { return mFrequencyMapping.toHertz(relativeFrequency); } protected long getCapabilities() { return mCapabilities; } private String[] getCapabilitiesNames() { List<String> names = new ArrayList<>(); if (hasCapability(IVibrator.CAP_ON_CALLBACK)) { Loading Loading @@ -370,7 +395,7 @@ public final class VibratorInfo implements Parcelable { * <p>The mapping is defined linearly by the following points: * * <ol> * <li>{@code toHertz(relativeMinFrequency} = minFrequency * <li>{@code toHertz(relativeMinFrequency) = minFrequency} * <li>{@code toHertz(-1) = resonantFrequency - safeRange / 2} * <li>{@code toHertz(0) = resonantFrequency} * <li>{@code toHertz(1) = resonantFrequency + safeRange / 2} Loading Loading @@ -555,6 +580,75 @@ public final class VibratorInfo implements Parcelable { }; } /** @hide */ public static final class Builder { private final int mId; private int mCapabilities = 0; private int[] mSupportedEffects = null; private int[] mSupportedBraking = null; private int[] mSupportedPrimitives = null; private float mQFactor = Float.NaN; private FrequencyMapping mFrequencyMapping = new FrequencyMapping(Float.NaN, Float.NaN, Float.NaN, Float.NaN, null); /** A builder class for a {@link VibratorInfo}. */ public Builder(int id) { mId = id; } /** Configure the vibrator capabilities with a combination of IVibrator.CAP_* values. */ @NonNull public Builder setCapabilities(int capabilities) { mCapabilities = capabilities; return this; } /** Configure the effects supported with {@link android.hardware.vibrator.Effect} values. */ @NonNull public Builder setSupportedEffects(int... supportedEffects) { mSupportedEffects = supportedEffects; return this; } /** Configure braking supported with {@link android.hardware.vibrator.Braking} values. */ @NonNull public Builder setSupportedBraking(int... supportedBraking) { mSupportedBraking = supportedBraking; return this; } /** * Configure the primitives supported with * {@link android.hardware.vibrator.CompositePrimitive} values. */ @NonNull public Builder setSupportedPrimitives(int... supportedPrimitives) { mSupportedPrimitives = supportedPrimitives; return this; } /** Configure the vibrator quality factor. */ @NonNull public Builder setQFactor(float qFactor) { mQFactor = qFactor; return this; } /** Configure the vibrator frequency information like resonant frequency and bandwidth. */ @NonNull public Builder setFrequencyMapping(FrequencyMapping frequencyMapping) { mFrequencyMapping = frequencyMapping; return this; } /** Build the configured {@link VibratorInfo}. */ @NonNull public VibratorInfo build() { return new VibratorInfo(mId, mCapabilities, mSupportedEffects, mSupportedBraking, mSupportedPrimitives, mQFactor, mFrequencyMapping); } } @NonNull public static final Creator<VibratorInfo> CREATOR = new Creator<VibratorInfo>() { Loading Loading
core/java/android/hardware/input/InputDeviceVibrator.java +13 −5 Original line number Diff line number Diff line Loading @@ -20,11 +20,13 @@ import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.app.ActivityThread; import android.content.Context; import android.hardware.vibrator.IVibrator; import android.os.Binder; import android.os.IVibratorStateListener; import android.os.VibrationAttributes; import android.os.VibrationEffect; import android.os.Vibrator; import android.os.VibratorInfo; import android.util.ArrayMap; import android.util.Log; Loading @@ -41,7 +43,7 @@ final class InputDeviceVibrator extends Vibrator { // mDeviceId represents InputDevice ID the vibrator belongs to private final int mDeviceId; private final int mVibratorId; private final VibratorInfo mVibratorInfo; private final Binder mToken; private final InputManager mInputManager; Loading @@ -52,7 +54,13 @@ final class InputDeviceVibrator extends Vibrator { InputDeviceVibrator(InputManager inputManager, int deviceId, int vibratorId) { mInputManager = inputManager; mDeviceId = deviceId; mVibratorId = vibratorId; mVibratorInfo = new VibratorInfo.Builder(vibratorId) .setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL) // Set predefined support to empty as we know input devices do not support them. .setSupportedEffects() .setSupportedPrimitives() .setSupportedBraking() .build(); mToken = new Binder(); } Loading @@ -74,8 +82,8 @@ final class InputDeviceVibrator extends Vibrator { } @Override public int getId() { return mVibratorId; protected VibratorInfo getInfo() { return mVibratorInfo; } @Override Loading Loading @@ -159,7 +167,7 @@ final class InputDeviceVibrator extends Vibrator { @Override public boolean hasAmplitudeControl() { return true; return mVibratorInfo.hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL); } /** Loading
core/java/android/os/SystemVibrator.java +75 −79 Original line number Diff line number Diff line Loading @@ -28,7 +28,6 @@ import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; import java.util.ArrayList; import java.util.Arrays; import java.util.Objects; import java.util.concurrent.Executor; Loading @@ -50,6 +49,9 @@ public class SystemVibrator extends Vibrator { private final ArrayMap<OnVibratorStateChangedListener, AllVibratorsStateListener> mRegisteredListeners = new ArrayMap<>(); private final Object mLock = new Object(); private AllVibratorsInfo mVibratorInfo; @UnsupportedAppUsage public SystemVibrator(Context context) { super(context); Loading @@ -57,6 +59,25 @@ public class SystemVibrator extends Vibrator { mVibratorManager = mContext.getSystemService(VibratorManager.class); } @Override protected VibratorInfo getInfo() { synchronized (mLock) { if (mVibratorInfo != null) { return mVibratorInfo; } if (mVibratorManager == null) { Log.w(TAG, "Failed to retrieve vibrator info; no vibrator manager."); return VibratorInfo.EMPTY_VIBRATOR_INFO; } int[] vibratorIds = mVibratorManager.getVibratorIds(); VibratorInfo[] vibratorInfos = new VibratorInfo[vibratorIds.length]; for (int i = 0; i < vibratorIds.length; i++) { vibratorInfos[i] = mVibratorManager.getVibrator(vibratorIds[i]).getInfo(); } return mVibratorInfo = new AllVibratorsInfo(vibratorInfos); } } @Override public boolean hasVibrator() { if (mVibratorManager == null) { Loading Loading @@ -144,20 +165,7 @@ public class SystemVibrator extends Vibrator { @Override public boolean hasAmplitudeControl() { if (mVibratorManager == null) { Log.w(TAG, "Failed to check vibrator has amplitude control; no vibrator manager."); return false; } int[] vibratorIds = mVibratorManager.getVibratorIds(); if (vibratorIds.length == 0) { return false; } for (int vibratorId : vibratorIds) { if (!mVibratorManager.getVibrator(vibratorId).hasAmplitudeControl()) { return false; } } return true; return getInfo().hasAmplitudeControl(); } @Override Loading @@ -183,70 +191,6 @@ public class SystemVibrator extends Vibrator { mVibratorManager.vibrate(uid, opPkg, combinedEffect, reason, attributes); } @Override public int[] areEffectsSupported(@VibrationEffect.EffectType int... effectIds) { int[] supported = new int[effectIds.length]; if (mVibratorManager == null) { Log.w(TAG, "Failed to check supported effects; no vibrator manager."); Arrays.fill(supported, Vibrator.VIBRATION_EFFECT_SUPPORT_NO); return supported; } int[] vibratorIds = mVibratorManager.getVibratorIds(); if (vibratorIds.length == 0) { Arrays.fill(supported, Vibrator.VIBRATION_EFFECT_SUPPORT_NO); return supported; } int[][] vibratorSupportMap = new int[vibratorIds.length][effectIds.length]; for (int i = 0; i < vibratorIds.length; i++) { vibratorSupportMap[i] = mVibratorManager.getVibrator( vibratorIds[i]).areEffectsSupported(effectIds); } Arrays.fill(supported, Vibrator.VIBRATION_EFFECT_SUPPORT_YES); for (int effectIdx = 0; effectIdx < effectIds.length; effectIdx++) { for (int vibratorIdx = 0; vibratorIdx < vibratorIds.length; vibratorIdx++) { int effectSupported = vibratorSupportMap[vibratorIdx][effectIdx]; if (effectSupported == Vibrator.VIBRATION_EFFECT_SUPPORT_NO) { supported[effectIdx] = Vibrator.VIBRATION_EFFECT_SUPPORT_NO; break; } else if (effectSupported == Vibrator.VIBRATION_EFFECT_SUPPORT_UNKNOWN) { supported[effectIdx] = Vibrator.VIBRATION_EFFECT_SUPPORT_UNKNOWN; } } } return supported; } @Override public boolean[] arePrimitivesSupported( @NonNull @VibrationEffect.Composition.PrimitiveType int... primitiveIds) { boolean[] supported = new boolean[primitiveIds.length]; if (mVibratorManager == null) { Log.w(TAG, "Failed to check supported primitives; no vibrator manager."); Arrays.fill(supported, false); return supported; } int[] vibratorIds = mVibratorManager.getVibratorIds(); if (vibratorIds.length == 0) { Arrays.fill(supported, false); return supported; } boolean[][] vibratorSupportMap = new boolean[vibratorIds.length][primitiveIds.length]; for (int i = 0; i < vibratorIds.length; i++) { vibratorSupportMap[i] = mVibratorManager.getVibrator( vibratorIds[i]).arePrimitivesSupported(primitiveIds); } Arrays.fill(supported, true); for (int primitiveIdx = 0; primitiveIdx < primitiveIds.length; primitiveIdx++) { for (int vibratorIdx = 0; vibratorIdx < vibratorIds.length; vibratorIdx++) { if (!vibratorSupportMap[vibratorIdx][primitiveIdx]) { supported[primitiveIdx] = false; break; } } } return supported; } @Override public void cancel() { if (mVibratorManager == null) { Loading Loading @@ -304,6 +248,58 @@ public class SystemVibrator extends Vibrator { } } /** * Represents all the vibrators information as a single {@link VibratorInfo}. * * <p>This uses the first vibrator on the list as the default one for all hardware spec, but * uses an intersection of all vibrators to decide the capabilities and effect/primitive * support. */ private static class AllVibratorsInfo extends VibratorInfo { private final VibratorInfo[] mVibratorInfos; AllVibratorsInfo(VibratorInfo[] vibrators) { super(/* id= */ -1, capabilitiesIntersection(vibrators), vibrators.length > 0 ? vibrators[0] : VibratorInfo.EMPTY_VIBRATOR_INFO); mVibratorInfos = vibrators; } @Override public int isEffectSupported(int effectId) { int supported = Vibrator.VIBRATION_EFFECT_SUPPORT_YES; for (VibratorInfo info : mVibratorInfos) { int effectSupported = info.isEffectSupported(effectId); if (effectSupported == Vibrator.VIBRATION_EFFECT_SUPPORT_NO) { return effectSupported; } else if (effectSupported == Vibrator.VIBRATION_EFFECT_SUPPORT_UNKNOWN) { supported = effectSupported; } } return supported; } @Override public boolean isPrimitiveSupported(int primitiveId) { for (VibratorInfo info : mVibratorInfos) { if (!info.isPrimitiveSupported(primitiveId)) { return false; } } return true; } private static int capabilitiesIntersection(VibratorInfo[] infos) { if (infos.length == 0) { return 0; } int intersection = ~0; for (VibratorInfo info : infos) { intersection &= info.getCapabilities(); } return intersection; } } /** Listener for all vibrators state change. */ private static class AllVibratorsStateListener { private final Object mLock = new Object(); Loading
core/java/android/os/SystemVibratorManager.java +2 −32 Original line number Diff line number Diff line Loading @@ -194,8 +194,8 @@ public class SystemVibratorManager extends VibratorManager { } @Override public int getId() { return mVibratorInfo.getId(); protected VibratorInfo getInfo() { return mVibratorInfo; } @Override Loading @@ -208,36 +208,6 @@ public class SystemVibratorManager extends VibratorManager { return mVibratorInfo.hasAmplitudeControl(); } @Override public float getResonantFrequency() { return mVibratorInfo.getResonantFrequency(); } @Override public float getQFactor() { return mVibratorInfo.getQFactor(); } @NonNull @Override public int[] areEffectsSupported(@NonNull int... effectIds) { int[] supported = new int[effectIds.length]; for (int i = 0; i < effectIds.length; i++) { supported[i] = mVibratorInfo.isEffectSupported(effectIds[i]); } return supported; } @Override public boolean[] arePrimitivesSupported( @NonNull @VibrationEffect.Composition.PrimitiveType int... primitiveIds) { boolean[] supported = new boolean[primitiveIds.length]; for (int i = 0; i < primitiveIds.length; i++) { supported[i] = mVibratorInfo.isPrimitiveSupported(primitiveIds[i]); } return supported; } @Override public boolean setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId, @Nullable VibrationEffect effect, @Nullable AudioAttributes attributes) { Loading
core/java/android/os/Vibrator.java +73 −9 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package android.os; import android.annotation.CallbackExecutor; import android.annotation.FloatRange; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; Loading @@ -26,12 +27,13 @@ import android.annotation.SystemService; import android.app.ActivityThread; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.hardware.vibrator.IVibrator; import android.media.AudioAttributes; import android.util.Log; import android.util.Range; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Arrays; import java.util.concurrent.Executor; /** Loading Loading @@ -156,6 +158,11 @@ public abstract class Vibrator { return ctx != null ? ctx.getResources().getInteger(resId) : VIBRATION_INTENSITY_MEDIUM; } /** @hide */ protected VibratorInfo getInfo() { return VibratorInfo.EMPTY_VIBRATOR_INFO; } /** * Get the default vibration intensity for haptic feedback. * Loading Loading @@ -190,7 +197,7 @@ public abstract class Vibrator { * service, or -1 this service is not attached to any physical vibrator. */ public int getId() { return -1; return getInfo().getId(); } /** Loading @@ -207,6 +214,18 @@ public abstract class Vibrator { */ public abstract boolean hasAmplitudeControl(); /** * Check whether the vibrator has independent frequency control. * * @return True if the hardware can control the frequency of the vibrations, otherwise false. * @hide */ public boolean hasFrequencyControl() { // We currently can only control frequency of the vibration using the compose PWLE method. return getInfo().hasCapability( IVibrator.CAP_FREQUENCY_CONTROL | IVibrator.CAP_COMPOSE_PWLE_EFFECTS); } /** * Gets the resonant frequency of the vibrator. * Loading @@ -215,7 +234,7 @@ public abstract class Vibrator { * @hide */ public float getResonantFrequency() { return Float.NaN; return getInfo().getResonantFrequency(); } /** Loading @@ -226,7 +245,44 @@ public abstract class Vibrator { * @hide */ public float getQFactor() { return Float.NaN; return getInfo().getQFactor(); } /** * Return a range of relative frequency values supported by the vibrator. * * <p>These values can be used to create waveforms that controls the vibration frequency via * {@link VibrationEffect.WaveformBuilder}. * * @return A range of relative frequency values supported. The range will always contain the * value 0, representing the device resonant frequency. Devices without frequency control will * return the range [0,0]. Devices with frequency control will always return a range containing * the safe range [-1, 1]. * @hide */ public Range<Float> getRelativeFrequencyRange() { return getInfo().getFrequencyRange(); } /** * Return the maximum amplitude the vibrator can play at given relative frequency. * * <p>Devices without frequency control will return 1 for the input zero (resonant frequency), * and 0 to any other input. * * <p>Devices with frequency control will return the supported value, for input in * {@link #getRelativeFrequencyRange()}, and 0 for any other input. * * <p>These values can be used to create waveforms that plays vibrations outside the resonant * frequency via {@link VibrationEffect.WaveformBuilder}. * * @return a value in [0,1] representing the maximum amplitude the device can play at given * relative frequency. * @hide */ @FloatRange(from = 0, to = 1) public float getMaximumAmplitude(float relativeFrequency) { return getInfo().getMaxAmplitude(relativeFrequency); } /** Loading Loading @@ -414,9 +470,12 @@ public abstract class Vibrator { @VibrationEffectSupport public int[] areEffectsSupported( @NonNull @VibrationEffect.EffectType int... effectIds) { final int[] support = new int[effectIds.length]; Arrays.fill(support, VIBRATION_EFFECT_SUPPORT_NO); return support; VibratorInfo info = getInfo(); int[] supported = new int[effectIds.length]; for (int i = 0; i < effectIds.length; i++) { supported[i] = info.isEffectSupported(effectIds[i]); } return supported; } /** Loading Loading @@ -468,7 +527,12 @@ public abstract class Vibrator { @NonNull public boolean[] arePrimitivesSupported( @NonNull @VibrationEffect.Composition.PrimitiveType int... primitiveIds) { return new boolean[primitiveIds.length]; VibratorInfo info = getInfo(); boolean[] supported = new boolean[primitiveIds.length]; for (int i = 0; i < primitiveIds.length; i++) { supported[i] = info.isPrimitiveSupported(primitiveIds[i]); } return supported; } /** Loading
core/java/android/os/VibratorInfo.java +96 −2 Original line number Diff line number Diff line Loading @@ -38,9 +38,12 @@ import java.util.Objects; * * @hide */ public final class VibratorInfo implements Parcelable { public class VibratorInfo implements Parcelable { private static final String TAG = "VibratorInfo"; /** @hide */ public static final VibratorInfo EMPTY_VIBRATOR_INFO = new VibratorInfo.Builder(-1).build(); private final int mId; private final long mCapabilities; @Nullable Loading Loading @@ -74,6 +77,23 @@ public final class VibratorInfo implements Parcelable { mFrequencyMapping = frequencyMapping; } protected VibratorInfo(int id, int capabilities, VibratorInfo baseVibrator) { mId = id; mCapabilities = capabilities; mSupportedEffects = baseVibrator.mSupportedEffects == null ? null : baseVibrator.mSupportedEffects.clone(); mSupportedBraking = baseVibrator.mSupportedBraking == null ? null : baseVibrator.mSupportedBraking.clone(); mSupportedPrimitives = baseVibrator.mSupportedPrimitives == null ? null : baseVibrator.mSupportedPrimitives.clone(); mQFactor = baseVibrator.mQFactor; mFrequencyMapping = new FrequencyMapping(baseVibrator.mFrequencyMapping.mMinFrequencyHz, baseVibrator.mFrequencyMapping.mResonantFrequencyHz, baseVibrator.mFrequencyMapping.mFrequencyResolutionHz, baseVibrator.mFrequencyMapping.mSuggestedSafeRangeHz, baseVibrator.mFrequencyMapping.mMaxAmplitudes); } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mId); Loading Loading @@ -145,6 +165,7 @@ public final class VibratorInfo implements Parcelable { * Returns a default value to be applied to composed PWLE effects for braking. * * @return a supported braking value, one of android.hardware.vibrator.Braking.* * @hide */ public int getDefaultBraking() { if (mSupportedBraking != null) { Loading Loading @@ -265,6 +286,10 @@ public final class VibratorInfo implements Parcelable { return mFrequencyMapping.toHertz(relativeFrequency); } protected long getCapabilities() { return mCapabilities; } private String[] getCapabilitiesNames() { List<String> names = new ArrayList<>(); if (hasCapability(IVibrator.CAP_ON_CALLBACK)) { Loading Loading @@ -370,7 +395,7 @@ public final class VibratorInfo implements Parcelable { * <p>The mapping is defined linearly by the following points: * * <ol> * <li>{@code toHertz(relativeMinFrequency} = minFrequency * <li>{@code toHertz(relativeMinFrequency) = minFrequency} * <li>{@code toHertz(-1) = resonantFrequency - safeRange / 2} * <li>{@code toHertz(0) = resonantFrequency} * <li>{@code toHertz(1) = resonantFrequency + safeRange / 2} Loading Loading @@ -555,6 +580,75 @@ public final class VibratorInfo implements Parcelable { }; } /** @hide */ public static final class Builder { private final int mId; private int mCapabilities = 0; private int[] mSupportedEffects = null; private int[] mSupportedBraking = null; private int[] mSupportedPrimitives = null; private float mQFactor = Float.NaN; private FrequencyMapping mFrequencyMapping = new FrequencyMapping(Float.NaN, Float.NaN, Float.NaN, Float.NaN, null); /** A builder class for a {@link VibratorInfo}. */ public Builder(int id) { mId = id; } /** Configure the vibrator capabilities with a combination of IVibrator.CAP_* values. */ @NonNull public Builder setCapabilities(int capabilities) { mCapabilities = capabilities; return this; } /** Configure the effects supported with {@link android.hardware.vibrator.Effect} values. */ @NonNull public Builder setSupportedEffects(int... supportedEffects) { mSupportedEffects = supportedEffects; return this; } /** Configure braking supported with {@link android.hardware.vibrator.Braking} values. */ @NonNull public Builder setSupportedBraking(int... supportedBraking) { mSupportedBraking = supportedBraking; return this; } /** * Configure the primitives supported with * {@link android.hardware.vibrator.CompositePrimitive} values. */ @NonNull public Builder setSupportedPrimitives(int... supportedPrimitives) { mSupportedPrimitives = supportedPrimitives; return this; } /** Configure the vibrator quality factor. */ @NonNull public Builder setQFactor(float qFactor) { mQFactor = qFactor; return this; } /** Configure the vibrator frequency information like resonant frequency and bandwidth. */ @NonNull public Builder setFrequencyMapping(FrequencyMapping frequencyMapping) { mFrequencyMapping = frequencyMapping; return this; } /** Build the configured {@link VibratorInfo}. */ @NonNull public VibratorInfo build() { return new VibratorInfo(mId, mCapabilities, mSupportedEffects, mSupportedBraking, mSupportedPrimitives, mQFactor, mFrequencyMapping); } } @NonNull public static final Creator<VibratorInfo> CREATOR = new Creator<VibratorInfo>() { Loading