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

Commit 0bac78b2 authored by Ahmad Khalil's avatar Ahmad Khalil
Browse files

Add new vibrator frequency profile for PWLE v2

This new vibrator frequency profile provides developers with richer information about the vibrators capabilities, including:
1. The minimum and maximum frequencies supported by the vibrator.
2. The maximum output acceleration the vibrator can achieve.
3. Retrieve frequency range for a specified minimum output acceleration

This profile will only be available if the device supports frequency control.

Bug: 347034419
Flag: android.os.vibrator.normalized_pwle_effects
Test: atest FrameworksVibratorCoreTests
Change-Id: I9a16b9b4932d95e8eb8c89bfa43f94920ed9e1ee
parent b0d339f5
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
@@ -34334,6 +34334,7 @@ package android.os {
    method @FlaggedApi("android.os.vibrator.normalized_pwle_effects") public boolean areEnvelopeEffectsSupported();
    method @NonNull public boolean[] arePrimitivesSupported(@NonNull int...);
    method @RequiresPermission(android.Manifest.permission.VIBRATE) public abstract void cancel();
    method @FlaggedApi("android.os.vibrator.normalized_pwle_effects") @Nullable public android.os.vibrator.VibratorFrequencyProfile getFrequencyProfile();
    method public int getId();
    method @FlaggedApi("android.os.vibrator.normalized_pwle_effects") public int getMaxEnvelopeEffectControlPointDurationMillis();
    method @FlaggedApi("android.os.vibrator.normalized_pwle_effects") public int getMaxEnvelopeEffectDurationMillis();
@@ -34687,6 +34688,19 @@ package android.os.strictmode {
}
package android.os.vibrator {
  @FlaggedApi("android.os.vibrator.normalized_pwle_effects") public final class VibratorFrequencyProfile {
    method @FlaggedApi("android.os.vibrator.normalized_pwle_effects") @NonNull public android.util.SparseArray<java.lang.Float> getFrequenciesOutputAcceleration();
    method @FlaggedApi("android.os.vibrator.normalized_pwle_effects") @Nullable public android.util.Range<java.lang.Float> getFrequencyRange(float);
    method @FlaggedApi("android.os.vibrator.normalized_pwle_effects") public float getMaxFrequencyHz();
    method @FlaggedApi("android.os.vibrator.normalized_pwle_effects") public float getMaxOutputAccelerationGs();
    method @FlaggedApi("android.os.vibrator.normalized_pwle_effects") public float getMinFrequencyHz();
    method @FlaggedApi("android.os.vibrator.normalized_pwle_effects") public float getOutputAccelerationGs(float);
  }
}
package android.preference {
  @Deprecated public class CheckBoxPreference extends android.preference.TwoStatePreference {
+23 −0
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@ import android.hardware.vibrator.IVibrator;
import android.media.AudioAttributes;
import android.os.vibrator.Flags;
import android.os.vibrator.VibrationConfig;
import android.os.vibrator.VibratorFrequencyProfile;
import android.os.vibrator.VibratorFrequencyProfileLegacy;
import android.util.Log;
import android.view.HapticFeedbackConstants;
@@ -302,6 +303,28 @@ public abstract class Vibrator {
        return new VibratorFrequencyProfileLegacy(frequencyProfile);
    }

    /**
     * Gets the profile that describes the vibrator output across the supported frequency range.
     *
     * <p>The profile describes the output acceleration that the device can reach when it
     * vibrates at different frequencies.
     *
     * @return The frequency profile for this vibrator, or null if the vibrator does not have
     * frequency control. If this vibrator is a composite of multiple physical devices then this
     * will return a profile supported in all devices, or null if the intersection is empty or not
     * available.
     */
    @FlaggedApi(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
    @Nullable
    public VibratorFrequencyProfile getFrequencyProfile() {
        VibratorInfo.FrequencyProfile frequencyProfile = getInfo().getFrequencyProfile();
        if (frequencyProfile.isEmpty()) {
            return null;
        }

        return new VibratorFrequencyProfile(frequencyProfile);
    }

    /**
     * Return the maximum amplitude the vibrator can play using the audio haptic channels.
     *
+354 −4
Original line number Diff line number Diff line
@@ -20,8 +20,10 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.hardware.vibrator.Braking;
import android.hardware.vibrator.IVibrator;
import android.os.vibrator.Flags;
import android.util.IndentingPrintWriter;
import android.util.MathUtils;
import android.util.Pair;
import android.util.Range;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
@@ -30,8 +32,11 @@ import com.android.internal.util.Preconditions;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.TreeMap;

/**
 * A VibratorInfo describes the capabilities of a {@link Vibrator}.
@@ -60,6 +65,7 @@ public class VibratorInfo implements Parcelable {
    private final int mPwleSizeMax;
    private final float mQFactor;
    private final FrequencyProfileLegacy mFrequencyProfileLegacy;
    private final FrequencyProfile mFrequencyProfile;
    private final int mMaxEnvelopeEffectSize;
    private final int mMinEnvelopeEffectControlPointDurationMillis;
    private final int mMaxEnvelopeEffectControlPointDurationMillis;
@@ -76,6 +82,7 @@ public class VibratorInfo implements Parcelable {
        mPwleSizeMax = in.readInt();
        mQFactor = in.readFloat();
        mFrequencyProfileLegacy = FrequencyProfileLegacy.CREATOR.createFromParcel(in);
        mFrequencyProfile = FrequencyProfile.CREATOR.createFromParcel(in);
        mMaxEnvelopeEffectSize = in.readInt();
        mMinEnvelopeEffectControlPointDurationMillis = in.readInt();
        mMaxEnvelopeEffectControlPointDurationMillis = in.readInt();
@@ -87,7 +94,7 @@ public class VibratorInfo implements Parcelable {
                baseVibratorInfo.mPrimitiveDelayMax, baseVibratorInfo.mCompositionSizeMax,
                baseVibratorInfo.mPwlePrimitiveDurationMax, baseVibratorInfo.mPwleSizeMax,
                baseVibratorInfo.mQFactor, baseVibratorInfo.mFrequencyProfileLegacy,
                baseVibratorInfo.mMaxEnvelopeEffectSize,
                baseVibratorInfo.mFrequencyProfile, baseVibratorInfo.mMaxEnvelopeEffectSize,
                baseVibratorInfo.mMinEnvelopeEffectControlPointDurationMillis,
                baseVibratorInfo.mMaxEnvelopeEffectControlPointDurationMillis);
    }
@@ -114,6 +121,17 @@ public class VibratorInfo implements Parcelable {
     * @param qFactor                  The vibrator quality factor.
     * @param frequencyProfileLegacy   The description of the vibrator supported frequencies and max
     *                                 amplitude mappings.
     * @param frequencyProfile       The description of the vibrator supported frequencies and
     *                                 output acceleration mappings.
     * @param maxEnvelopeEffectSize    The maximum number of control points supported for an
     *                                 envelope effect.
     * @param minEnvelopeEffectControlPointDurationMillis   The minimum duration supported
     *                                                      between two control points within an
     *                                                      envelope effect.
     * @param maxEnvelopeEffectControlPointDurationMillis   The maximum duration supported
     *                                                      between two control points within an
     *                                                      envelope effect.
     *
     * @hide
     */
    public VibratorInfo(int id, long capabilities, @Nullable SparseBooleanArray supportedEffects,
@@ -121,10 +139,12 @@ public class VibratorInfo implements Parcelable {
            @NonNull SparseIntArray supportedPrimitives, int primitiveDelayMax,
            int compositionSizeMax, int pwlePrimitiveDurationMax, int pwleSizeMax,
            float qFactor, @NonNull FrequencyProfileLegacy frequencyProfileLegacy,
            int maxEnvelopeEffectSize, int minEnvelopeEffectControlPointDurationMillis,
            @NonNull FrequencyProfile frequencyProfile, int maxEnvelopeEffectSize,
            int minEnvelopeEffectControlPointDurationMillis,
            int maxEnvelopeEffectControlPointDurationMillis) {
        Preconditions.checkNotNull(supportedPrimitives);
        Preconditions.checkNotNull(frequencyProfileLegacy);
        Preconditions.checkNotNull(frequencyProfile);
        mId = id;
        mCapabilities = capabilities;
        mSupportedEffects = supportedEffects == null ? null : supportedEffects.clone();
@@ -136,6 +156,7 @@ public class VibratorInfo implements Parcelable {
        mPwleSizeMax = pwleSizeMax;
        mQFactor = qFactor;
        mFrequencyProfileLegacy = frequencyProfileLegacy;
        mFrequencyProfile = frequencyProfile;
        mMaxEnvelopeEffectSize = maxEnvelopeEffectSize;
        mMinEnvelopeEffectControlPointDurationMillis =
                minEnvelopeEffectControlPointDurationMillis;
@@ -156,6 +177,7 @@ public class VibratorInfo implements Parcelable {
        dest.writeInt(mPwleSizeMax);
        dest.writeFloat(mQFactor);
        mFrequencyProfileLegacy.writeToParcel(dest, flags);
        mFrequencyProfile.writeToParcel(dest, flags);
        dest.writeInt(mMaxEnvelopeEffectSize);
        dest.writeInt(mMinEnvelopeEffectControlPointDurationMillis);
        dest.writeInt(mMaxEnvelopeEffectControlPointDurationMillis);
@@ -206,6 +228,7 @@ public class VibratorInfo implements Parcelable {
                && Objects.equals(mSupportedBraking, that.mSupportedBraking)
                && Objects.equals(mQFactor, that.mQFactor)
                && Objects.equals(mFrequencyProfileLegacy, that.mFrequencyProfileLegacy)
                && Objects.equals(mFrequencyProfile, that.mFrequencyProfile)
                && mMaxEnvelopeEffectSize == that.mMaxEnvelopeEffectSize
                && mMinEnvelopeEffectControlPointDurationMillis
                == that.mMinEnvelopeEffectControlPointDurationMillis
@@ -216,7 +239,7 @@ public class VibratorInfo implements Parcelable {
    @Override
    public int hashCode() {
        int hashCode = Objects.hash(mId, mCapabilities, mSupportedEffects, mSupportedBraking,
                mQFactor, mFrequencyProfileLegacy);
                mQFactor, mFrequencyProfileLegacy, mFrequencyProfile);
        for (int i = 0; i < mSupportedPrimitives.size(); i++) {
            hashCode = 31 * hashCode + mSupportedPrimitives.keyAt(i);
            hashCode = 31 * hashCode + mSupportedPrimitives.valueAt(i);
@@ -239,6 +262,7 @@ public class VibratorInfo implements Parcelable {
                + ", mPwleSizeMax=" + mPwleSizeMax
                + ", mQFactor=" + mQFactor
                + ", mFrequencyProfileLegacy=" + mFrequencyProfileLegacy
                + ", mFrequencyProfile=" + mFrequencyProfile
                + ", mMaxEnvelopeEffectSize=" + mMaxEnvelopeEffectSize
                + ", mMinEnvelopeEffectControlPointDurationMillis="
                + mMinEnvelopeEffectControlPointDurationMillis
@@ -263,6 +287,7 @@ public class VibratorInfo implements Parcelable {
        pw.println("pwleSizeMax = " + mPwleSizeMax);
        pw.println("q-factor = " + mQFactor);
        pw.println("frequencyProfileLegacy = " + mFrequencyProfileLegacy);
        pw.println("frequencyProfile = " + mFrequencyProfile);
        pw.println("mMaxEnvelopeEffectSize = " + mMaxEnvelopeEffectSize);
        pw.println("mMinEnvelopeEffectControlPointDurationMillis = "
                + mMinEnvelopeEffectControlPointDurationMillis);
@@ -517,6 +542,9 @@ public class VibratorInfo implements Parcelable {
     * this vibrator is a composite of multiple physical devices.
     */
    public float getResonantFrequencyHz() {
        if (Flags.normalizedPwleEffects()) {
            return mFrequencyProfile.mResonantFrequencyHz;
        }
        return mFrequencyProfileLegacy.mResonantFrequencyHz;
    }

@@ -541,6 +569,17 @@ public class VibratorInfo implements Parcelable {
        return mFrequencyProfileLegacy;
    }

    /**
     * Gets the profile of supported frequencies, including the measurements of maximum
     * output acceleration for supported vibration frequencies.
     *
     * <p>If the devices does not have frequency control then the profile should be empty.
     */
    @NonNull
    public FrequencyProfile getFrequencyProfile() {
        return mFrequencyProfile;
    }

    /** Returns a single int representing all the capabilities of the vibrator. */
    public long getCapabilities() {
        return mCapabilities;
@@ -622,6 +661,304 @@ public class VibratorInfo implements Parcelable {
        return names;
    }

    /**
     * Describes the maximum output acceleration that can be achieved for each supported
     * frequency in a specific vibrator.
     *
     * @hide
     */
    public static final class FrequencyProfile implements Parcelable {

        private final float[] mFrequenciesHz;
        private final float[] mOutputAccelerationsGs;
        private final float mResonantFrequencyHz;
        private final float mMaxOutputAccelerationGs;
        private final float mMinFrequencyHz;
        private final float mMaxFrequencyHz;

        public FrequencyProfile(Parcel in) {
            this(in.readFloat(), in.createFloatArray(), in.createFloatArray());
        }

        /**
         * Default constructor.
         *
         * @param resonantFrequencyHz   The vibrator resonant frequency, in hertz.
         * @param frequenciesHz         The supported vibration frequencies, in hertz.
         * @param outputAccelerationsGs The maximum achievable output acceleration (in Gs) the
         *                              device can reach at the supported frequencies.
         */
        public FrequencyProfile(float resonantFrequencyHz, float[] frequenciesHz,
                float[] outputAccelerationsGs) {

            mResonantFrequencyHz = resonantFrequencyHz;

            boolean isValid = !Float.isNaN(resonantFrequencyHz)
                    && (resonantFrequencyHz > 0)
                    && (frequenciesHz != null && outputAccelerationsGs != null)
                    && (frequenciesHz.length == outputAccelerationsGs.length)
                    && (frequenciesHz.length > 0);

            if (!isValid) {
                mFrequenciesHz = null;
                mOutputAccelerationsGs = null;
                mMinFrequencyHz = Float.NaN;
                mMaxFrequencyHz = Float.NaN;
                mMaxOutputAccelerationGs = Float.NaN;
                return;
            }

            TreeMap<Float, Float> frequencyToOutputAccelerationMap = new TreeMap<>();

            for (int i = 0; i < frequenciesHz.length; i++) {
                frequencyToOutputAccelerationMap.putIfAbsent(frequenciesHz[i],
                        outputAccelerationsGs[i]);
            }

            float[] frequencies = new float[frequencyToOutputAccelerationMap.size()];
            float[] accelerations = new float[frequencyToOutputAccelerationMap.size()];
            float maxOutputAccelerationGs = 0;
            int i = 0;
            for (Map.Entry<Float, Float> entry : frequencyToOutputAccelerationMap.entrySet()) {
                frequencies[i] = entry.getKey();
                accelerations[i] = entry.getValue();
                maxOutputAccelerationGs = Math.max(maxOutputAccelerationGs, entry.getValue());
                i++;
            }

            mFrequenciesHz = frequencies;
            mOutputAccelerationsGs = accelerations;
            mMinFrequencyHz = mFrequenciesHz[0];
            mMaxFrequencyHz = mFrequenciesHz[mFrequenciesHz.length - 1];
            mMaxOutputAccelerationGs = maxOutputAccelerationGs;
        }

        /** Returns true if the supported frequency range is null. */
        public boolean isEmpty() {
            return mFrequenciesHz == null;
        }

        /**
         * Returns a list of available frequencies.
         */
        @Nullable
        public float[] getFrequenciesHz() {
            return mFrequenciesHz;
        }

        /** Returns the list of available output accelerations */
        @Nullable
        public float[] getOutputAccelerationsGs() {
            return mOutputAccelerationsGs;
        }

        /** Maximum output acceleration reachable in Gs when amplitude is 1.0f. */
        public float getMaxOutputAccelerationGs() {
            return mMaxOutputAccelerationGs;
        }

        /**
         * Calculates the maximum output acceleration for a given frequency using linear
         * interpolation.
         *
         * @param frequencyHz frequency, in hertz, for query.
         * @return the maximum output acceleration for the given frequency.
         */
        public float getOutputAccelerationGs(float frequencyHz) {
            if (mFrequenciesHz == null) {
                return Float.NaN;
            }

            if (frequencyHz < mMinFrequencyHz || frequencyHz > mMaxFrequencyHz) {
                // Outside supported frequency range, not able to vibrate at this frequency.
                return 0;
            }

            int idx = Arrays.binarySearch(mFrequenciesHz, frequencyHz);
            if (idx >= 0) {
                return mOutputAccelerationsGs[idx];
            }

            // This indicates that the value was not found in the list. Adjust index of the
            // insertion point to be at the lower bound.
            idx = -idx - 2;

            // Linearly interpolate the output acceleration based on the frequency.
            return MathUtils.constrainedMap(
                    mOutputAccelerationsGs[idx], mOutputAccelerationsGs[idx + 1],
                    mFrequenciesHz[idx], mFrequenciesHz[idx + 1],
                    frequencyHz);
        }

        /** The minimum frequency supported, in hertz. */
        public float getMinFrequencyHz() {
            return mMinFrequencyHz;
        }

        /** The maximum frequency supported, in hertz. */
        public float getMaxFrequencyHz() {
            return mMaxFrequencyHz;
        }

        /**
         * Returns the frequency range that supports the specified minimum output
         * acceleration.
         *
         * @return The frequency range, or null if the specified acceleration
         *         is not achievable on the device.
         */
        @Nullable
        public Range<Float> getFrequencyRangeHz(float minOutputAcceleration) {
            if (mFrequenciesHz == null || mOutputAccelerationsGs == null
                    || minOutputAcceleration > mMaxOutputAccelerationGs) {
                return null; // No frequency range available
            }

            if (minOutputAcceleration <= 0) {
                return new Range<>(mMinFrequencyHz, mMaxFrequencyHz);
            }

            float minFrequency = Float.NaN;
            float maxFrequency = Float.NaN;
            int lowerFrequencyBoundIndex = 0;
            // Find the lower frequency bound
            for (int i = 0; i < mOutputAccelerationsGs.length; i++) {
                if (mOutputAccelerationsGs[i] >= minOutputAcceleration) {
                    if (i == 0) {
                        minFrequency = mMinFrequencyHz;
                    } else {
                        minFrequency = MathUtils.constrainedMap(
                                mFrequenciesHz[i - 1], mFrequenciesHz[i],
                                mOutputAccelerationsGs[i - 1], mOutputAccelerationsGs[i],
                                minOutputAcceleration);
                    }
                    lowerFrequencyBoundIndex = i;
                    break; // Found the lower bound
                }
            }

            if (Float.isNaN(minFrequency)) {
                // Lower bound was not found
                return null;
            }

            // Find the upper frequency bound
            for (int i = lowerFrequencyBoundIndex; i < mOutputAccelerationsGs.length; i++) {
                if (mOutputAccelerationsGs[i] <= minOutputAcceleration) {
                    maxFrequency = MathUtils.constrainedMap(
                            mFrequenciesHz[i - 1], mFrequenciesHz[i],
                            mOutputAccelerationsGs[i - 1], mOutputAccelerationsGs[i],
                            minOutputAcceleration);
                    break; // Found the upper bound
                }
            }

            if (Float.isNaN(maxFrequency)) {
                // If the upper bound was not found, the specified output acceleration is
                // achievable at all remaining frequencies.
                maxFrequency = mMaxFrequencyHz;
            }

            return new Range<>(minFrequency, maxFrequency);
        }

        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeFloat(mResonantFrequencyHz);
            dest.writeFloatArray(mFrequenciesHz);
            dest.writeFloatArray(mOutputAccelerationsGs);
        }

        @Override
        public int describeContents() {
            return 0;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }
            if (!(o instanceof FrequencyProfile that)) {
                return false;
            }
            return Float.compare(mResonantFrequencyHz, that.mResonantFrequencyHz) == 0
                    && Arrays.equals(mFrequenciesHz, that.mFrequenciesHz)
                    && Arrays.equals(mOutputAccelerationsGs, that.mOutputAccelerationsGs);
        }

        @Override
        public int hashCode() {
            return Objects.hash(mResonantFrequencyHz, Arrays.hashCode(mFrequenciesHz),
                    Arrays.hashCode(mOutputAccelerationsGs));
        }

        @Override
        public String toString() {
            return "FrequencyProfile{"
                    + "mResonantFrequency=" + mResonantFrequencyHz
                    + ", mFrequenciesHz=" + Arrays.toString(mFrequenciesHz)
                    + ", mOutputAccelerationsGs=" + Arrays.toString(mOutputAccelerationsGs)
                    + ", mMinFrequencyHz=" + mMinFrequencyHz
                    + ", mMaxFrequencyHz=" + mMaxFrequencyHz
                    + ", mMaxOutputAccelerationGs=" + mMaxOutputAccelerationGs
                    + '}';
        }

        @NonNull
        public static final Creator<FrequencyProfile> CREATOR =
                new Creator<FrequencyProfile>() {
                    @Override
                    public FrequencyProfile createFromParcel(Parcel in) {
                        return new FrequencyProfile(in);
                    }

                    @Override
                    public FrequencyProfile[] newArray(int size) {
                        return new FrequencyProfile[size];
                    }
                };

        private static void deduplicateAndSortList(List<Pair<Float, Float>> list) {
            if (list == null || list.size() < 2) {
                return; // Nothing to dedupe
            }

            list.sort(Comparator.comparing(pair -> pair.first));

            // Remove duplicates from the list
            int writeIndex = 1;
            for (int i = 1; i < list.size(); i++) {
                Pair<Float, Float> currentPair = list.get(i);
                Pair<Float, Float> previousPair = list.get(writeIndex - 1);

                if (currentPair.first.compareTo(previousPair.first) != 0) {
                    list.set(writeIndex++, currentPair);
                }
            }
            list.subList(writeIndex, list.size()).clear();
        }

        private static ArrayList<Pair<Float, Float>> extractFrequencyToOutputAccelerationData(
                float[] frequencies, float[] outputAccelerations) {

            if (frequencies == null || outputAccelerations == null
                    || frequencies.length == 0
                    || frequencies.length != outputAccelerations.length) {
                return new ArrayList<>(); // Return empty list for invalid or mismatched data
            }

            ArrayList<Pair<Float, Float>> frequencyToOutputAccelerationList = new ArrayList<>(
                    frequencies.length);
            for (int i = 0; i < frequencies.length; i++) {
                frequencyToOutputAccelerationList.add(
                        new Pair<>(frequencies[i], outputAccelerations[i]));
            }

            return frequencyToOutputAccelerationList;
        }
    }

    /**
     * Describes the maximum relative output acceleration that can be achieved for each supported
     * frequency in a specific vibrator.
@@ -834,6 +1171,8 @@ public class VibratorInfo implements Parcelable {
        private float mQFactor = Float.NaN;
        private FrequencyProfileLegacy mFrequencyProfileLegacy =
                new FrequencyProfileLegacy(Float.NaN, Float.NaN, Float.NaN, null);
        private FrequencyProfile mFrequencyProfile = new FrequencyProfile(Float.NaN, null,
                null);
        private int mMaxEnvelopeEffectSize;
        private int mMinEnvelopeEffectControlPointDurationMillis;
        private int mMaxEnvelopeEffectControlPointDurationMillis;
@@ -913,6 +1252,16 @@ public class VibratorInfo implements Parcelable {
            return this;
        }

        /**
         * Configure the vibrator frequency information like resonant frequency and frequency to
         * output acceleration data.
         */
        @NonNull
        public Builder setFrequencyProfile(@NonNull FrequencyProfile frequencyProfile) {
            mFrequencyProfile = frequencyProfile;
            return this;
        }

        /**
         * Configure the maximum number of control points supported for envelope effects on this
         * device.
@@ -951,7 +1300,8 @@ public class VibratorInfo implements Parcelable {
            return new VibratorInfo(mId, mCapabilities, mSupportedEffects, mSupportedBraking,
                    mSupportedPrimitives, mPrimitiveDelayMax, mCompositionSizeMax,
                    mPwlePrimitiveDurationMax, mPwleSizeMax, mQFactor, mFrequencyProfileLegacy,
                    mMaxEnvelopeEffectSize, mMinEnvelopeEffectControlPointDurationMillis,
                    mFrequencyProfile, mMaxEnvelopeEffectSize,
                    mMinEnvelopeEffectControlPointDurationMillis,
                    mMaxEnvelopeEffectControlPointDurationMillis);
        }

Loading