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

Commit f6febe9b authored by Lais Andrade's avatar Lais Andrade Committed by Android (Google) Code Review
Browse files

Merge "Expose vibrator frequency bandwith to the service" into sc-dev

parents b4477092 853dcb5a
Loading
Loading
Loading
Loading
+13 −5
Original line number Diff line number Diff line
@@ -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;

@@ -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;

@@ -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();
    }

@@ -74,8 +82,8 @@ final class InputDeviceVibrator extends Vibrator {
    }

    @Override
    public int getId() {
        return mVibratorId;
    protected VibratorInfo getInfo() {
        return mVibratorInfo;
    }

    @Override
@@ -159,7 +167,7 @@ final class InputDeviceVibrator extends Vibrator {

    @Override
    public boolean hasAmplitudeControl() {
        return true;
        return mVibratorInfo.hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL);
    }

    /**
+75 −79
Original line number Diff line number Diff line
@@ -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;

@@ -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);
@@ -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) {
@@ -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
@@ -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) {
@@ -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();
+2 −32
Original line number Diff line number Diff line
@@ -194,8 +194,8 @@ public class SystemVibratorManager extends VibratorManager {
        }

        @Override
        public int getId() {
            return mVibratorInfo.getId();
        protected VibratorInfo getInfo() {
            return mVibratorInfo;
        }

        @Override
@@ -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) {
+73 −9
Original line number Diff line number Diff line
@@ -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;
@@ -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;

/**
@@ -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.
     *
@@ -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();
    }

    /**
@@ -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.
     *
@@ -215,7 +234,7 @@ public abstract class Vibrator {
     * @hide
     */
    public float getResonantFrequency() {
        return Float.NaN;
        return getInfo().getResonantFrequency();
    }

    /**
@@ -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);
    }

    /**
@@ -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;
    }

    /**
@@ -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;
    }

    /**
+96 −2
Original line number Diff line number Diff line
@@ -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
@@ -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);
@@ -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) {
@@ -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)) {
@@ -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}
@@ -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