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

Commit bdcf3d91 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Automerger Merge Worker
Browse files

Merge "Use PWLE for waveforms and add ramp down to zero" into sc-dev am: 5bb0024e

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/14734461

Change-Id: I52a0bbbead9b3235a294e392ede3f45338fefb64
parents 350b2666 5bb0024e
Loading
Loading
Loading
Loading
+9 −0
Original line number Original line Diff line number Diff line
@@ -3354,6 +3354,15 @@
     (e.g. accessibility, alarms). This is mainly for Wear devices that don't have speakers. -->
     (e.g. accessibility, alarms). This is mainly for Wear devices that don't have speakers. -->
    <bool name="config_allowPriorityVibrationsInLowPowerMode">false</bool>
    <bool name="config_allowPriorityVibrationsInLowPowerMode">false</bool>


    <!-- The duration (in milliseconds) that should be used to convert vibration ramps to a sequence
         of fixed amplitude steps on devices without PWLE support. -->
    <integer name="config_vibrationWaveformRampStepDuration">5</integer>

    <!-- The duration (in milliseconds) that should be applied to waveform vibrations that ends in
         non-zero amplitudes, . The waveform will
         be played as a PWLE instead of on/off calls if this value is set. -->
    <integer name="config_vibrationWaveformRampDownDuration">0</integer>

    <!-- Number of retries Cell Data should attempt for a given error code before
    <!-- Number of retries Cell Data should attempt for a given error code before
         restarting the modem.
         restarting the modem.
         Error codes not listed will not lead to modem restarts.
         Error codes not listed will not lead to modem restarts.
+2 −0
Original line number Original line Diff line number Diff line
@@ -2012,6 +2012,8 @@
  <java-symbol type="integer" name="config_notificationServiceArchiveSize" />
  <java-symbol type="integer" name="config_notificationServiceArchiveSize" />
  <java-symbol type="integer" name="config_previousVibrationsDumpLimit" />
  <java-symbol type="integer" name="config_previousVibrationsDumpLimit" />
  <java-symbol type="integer" name="config_defaultVibrationAmplitude" />
  <java-symbol type="integer" name="config_defaultVibrationAmplitude" />
  <java-symbol type="integer" name="config_vibrationWaveformRampStepDuration" />
  <java-symbol type="integer" name="config_vibrationWaveformRampDownDuration" />
  <java-symbol type="integer" name="config_radioScanningTimeout" />
  <java-symbol type="integer" name="config_radioScanningTimeout" />
  <java-symbol type="integer" name="config_screenBrightnessSettingMinimum" />
  <java-symbol type="integer" name="config_screenBrightnessSettingMinimum" />
  <java-symbol type="integer" name="config_screenBrightnessSettingMaximum" />
  <java-symbol type="integer" name="config_screenBrightnessSettingMaximum" />
+6 −6
Original line number Original line Diff line number Diff line
@@ -16,6 +16,7 @@


package com.android.server.vibrator;
package com.android.server.vibrator;


import android.content.Context;
import android.os.VibrationEffect;
import android.os.VibrationEffect;
import android.os.VibratorInfo;
import android.os.VibratorInfo;


@@ -26,17 +27,16 @@ import java.util.List;
final class DeviceVibrationEffectAdapter
final class DeviceVibrationEffectAdapter
        implements VibrationEffectAdapters.EffectAdapter<VibratorInfo> {
        implements VibrationEffectAdapters.EffectAdapter<VibratorInfo> {


    /** Duration of each step created to simulate a ramp segment. */
    private static final int RAMP_STEP_DURATION_MILLIS = 5;

    private final List<VibrationEffectAdapters.SegmentsAdapter<VibratorInfo>> mSegmentAdapters;
    private final List<VibrationEffectAdapters.SegmentsAdapter<VibratorInfo>> mSegmentAdapters;


    DeviceVibrationEffectAdapter() {
    DeviceVibrationEffectAdapter(Context context) {
        mSegmentAdapters = Arrays.asList(
        mSegmentAdapters = Arrays.asList(
                // TODO(b/167947076): add filter that removes unsupported primitives
                // TODO(b/167947076): add filter that removes unsupported primitives
                // TODO(b/167947076): add filter that replaces unsupported prebaked with fallback
                // TODO(b/167947076): add filter that replaces unsupported prebaked with fallback
                new RampToStepAdapter(RAMP_STEP_DURATION_MILLIS),
                new RampToStepAdapter(context.getResources().getInteger(
                new StepToRampAdapter(),
                        com.android.internal.R.integer.config_vibrationWaveformRampStepDuration)),
                new StepToRampAdapter(context.getResources().getInteger(
                        com.android.internal.R.integer.config_vibrationWaveformRampDownDuration)),
                new ClippingAmplitudeAndFrequencyAdapter()
                new ClippingAmplitudeAndFrequencyAdapter()
        );
        );
    }
    }
+1 −1
Original line number Original line Diff line number Diff line
@@ -29,7 +29,7 @@ import java.util.List;
/**
/**
 * Adapter that converts ramp segments that to a sequence of fixed step segments.
 * Adapter that converts ramp segments that to a sequence of fixed step segments.
 *
 *
 * <p>This leaves the list unchanged if the device have compose PWLE capability.
 * <p>This leaves the list unchanged if the device has compose PWLE capability.
 */
 */
final class RampToStepAdapter implements VibrationEffectAdapters.SegmentsAdapter<VibratorInfo> {
final class RampToStepAdapter implements VibrationEffectAdapters.SegmentsAdapter<VibratorInfo> {


+189 −7
Original line number Original line Diff line number Diff line
@@ -27,39 +27,221 @@ import java.util.List;
/**
/**
 * Adapter that converts step segments that should be handled as PWLEs to ramp segments.
 * Adapter that converts step segments that should be handled as PWLEs to ramp segments.
 *
 *
 * <p>This leaves the list unchanged if the device do not have compose PWLE capability.
 * <p>Each replaced {@link StepSegment} will be represented by a {@link RampSegment} with same
 * start and end amplitudes/frequencies, which can then be converted to PWLE compositions. This
 * adapter leaves the segments unchanged if the device doesn't have the PWLE composition capability.
 *
 * <p>This adapter also applies the ramp down duration config on devices with PWLE support. This
 * prevents the device from ringing when it cannot handle abrupt changes between ON and OFF states.
 * This will not change other types of abrupt amplitude changes in the original effect.
 *
 * <p>The effect overall duration is preserved by this transformation. Waveforms with ON/OFF
 * segments are handled gracefully by the ramp down changes. Each OFF segment preceded by an ON
 * segment will be shortened, and a ramp down will be added to the transition between ON and OFF.
 * The ramps can be shorter than the configured duration in order to preserve the waveform timings,
 * but they will still soften the ringing effect.
 */
 */
final class StepToRampAdapter implements VibrationEffectAdapters.SegmentsAdapter<VibratorInfo> {
final class StepToRampAdapter implements VibrationEffectAdapters.SegmentsAdapter<VibratorInfo> {

    private final int mRampDownDuration;

    StepToRampAdapter(int rampDownDuration) {
        mRampDownDuration = rampDownDuration;
    }

    @Override
    @Override
    public int apply(List<VibrationEffectSegment> segments, int repeatIndex,
    public int apply(List<VibrationEffectSegment> segments, int repeatIndex,
            VibratorInfo info) {
            VibratorInfo info) {
        if (!info.hasCapability(IVibrator.CAP_COMPOSE_PWLE_EFFECTS)) {
        if (!info.hasCapability(IVibrator.CAP_COMPOSE_PWLE_EFFECTS)) {
            // The vibrator do not have PWLE capability, so keep the segments unchanged.
            // The vibrator does not have PWLE capability, so keep the segments unchanged.
            return repeatIndex;
            return repeatIndex;
        }
        }
        convertStepsToRamps(segments);
        int newRepeatIndex = addRampDownToZeroAmplitudeSegments(segments, repeatIndex);
        newRepeatIndex = addRampDownToLoop(segments, newRepeatIndex);
        return newRepeatIndex;
    }

    private void convertStepsToRamps(List<VibrationEffectSegment> segments) {
        int segmentCount = segments.size();
        int segmentCount = segments.size();
        if (mRampDownDuration > 0) {
            // Convert all steps to ramps if the device requires ramp down.
            for (int i = 0; i < segmentCount; i++) {
                if (isStep(segments.get(i))) {
                    segments.set(i, apply((StepSegment) segments.get(i)));
                }
            }
            return;
        }
        // Convert steps that require frequency control to ramps.
        // Convert steps that require frequency control to ramps.
        for (int i = 0; i < segmentCount; i++) {
        for (int i = 0; i < segmentCount; i++) {
            VibrationEffectSegment segment = segments.get(i);
            VibrationEffectSegment segment = segments.get(i);
            if ((segment instanceof StepSegment)
            if (isStep(segment) && ((StepSegment) segment).getFrequency() != 0) {
                    && ((StepSegment) segment).getFrequency() != 0) {
                segments.set(i, apply((StepSegment) segment));
                segments.set(i, apply((StepSegment) segment));
            }
            }
        }
        }
        // Convert steps that are next to ramps to also become ramps, so they can be composed
        // Convert steps that are next to ramps to also become ramps, so they can be composed
        // together in the same PWLE waveform.
        // together in the same PWLE waveform.
        for (int i = 1; i < segmentCount; i++) {
        for (int i = 0; i < segmentCount; i++) {
            if (segments.get(i) instanceof RampSegment) {
            if (segments.get(i) instanceof RampSegment) {
                for (int j = i - 1; j >= 0 && (segments.get(j) instanceof StepSegment); j--) {
                for (int j = i - 1; j >= 0 && isStep(segments.get(j)); j--) {
                    segments.set(j, apply((StepSegment) segments.get(j)));
                }
                for (int j = i + 1; j < segmentCount && isStep(segments.get(j)); j++) {
                    segments.set(j, apply((StepSegment) segments.get(j)));
                    segments.set(j, apply((StepSegment) segments.get(j)));
                }
                }
            }
            }
        }
        }
    }

    /**
     * This will add a ramp to zero as follows:
     *
     * <ol>
     *     <li>Remove the {@link VibrationEffectSegment} that starts and ends at zero amplitude
     *         and follows a segment that ends at non-zero amplitude;
     *     <li>Add a ramp down to zero starting at the previous segment end amplitude and frequency,
     *         with min between the removed segment duration and the configured ramp down duration;
     *     <li>Add a zero amplitude segment following the ramp with the remaining duration, if
     *         necessary;
     * </ol>
     */
    private int addRampDownToZeroAmplitudeSegments(List<VibrationEffectSegment> segments,
            int repeatIndex) {
        if (mRampDownDuration <= 0) {
            // Nothing to do, no ramp down duration configured.
            return repeatIndex;
            return repeatIndex;
        }
        }
        int newRepeatIndex = repeatIndex;
        int newSegmentCount = segments.size();
        for (int i = 1; i < newSegmentCount; i++) {
            if (!isOffRampSegment(segments.get(i))
                    || !endsWithNonZeroAmplitude(segments.get(i - 1))) {
                continue;
            }

            // We know the previous segment is a ramp that ends at non-zero amplitude.
            float previousAmplitude = ((RampSegment) segments.get(i - 1)).getEndAmplitude();
            float previousFrequency = ((RampSegment) segments.get(i - 1)).getEndFrequency();
            RampSegment ramp = (RampSegment) segments.get(i);

            if (ramp.getDuration() <= mRampDownDuration) {
                // Replace the zero amplitude segment with a ramp down of same duration, to
                // preserve waveform timings and still soften the transition to zero.
                segments.set(i, createRampDown(previousAmplitude, previousFrequency,
                        ramp.getDuration()));
            } else {
                // Make the zero amplitude segment shorter, to preserve waveform timings, and add a
                // ramp down to zero segment right before it.
                segments.set(i, updateDuration(ramp, ramp.getDuration() - mRampDownDuration));
                segments.add(i, createRampDown(previousAmplitude, previousFrequency,
                        mRampDownDuration));
                if (repeatIndex > i) {
                    newRepeatIndex++;
                }
                i++;
                newSegmentCount++;
            }
        }
        return newRepeatIndex;
    }


    private RampSegment apply(StepSegment segment) {
    /**
     * This will add a ramp to zero at the repeating index of the given effect, if set, only if
     * the last segment ends at a non-zero amplitude and the repeating segment starts and ends at
     * zero amplitude. The update is described as:
     *
     * <ol>
     *     <li>Add a ramp down to zero following the last segment, with the min between the
     *         removed segment duration and the configured ramp down duration;
     *     <li>Skip the zero-amplitude segment by incrementing the repeat index, splitting it if
     *         necessary to skip the correct amount;
     * </ol>
     */
    private int addRampDownToLoop(List<VibrationEffectSegment> segments, int repeatIndex) {
        if (repeatIndex < 0) {
            // Non-repeating compositions should remain unchanged so duration will be preserved.
            return repeatIndex;
        }

        int segmentCount = segments.size();
        if (mRampDownDuration <= 0 || !endsWithNonZeroAmplitude(segments.get(segmentCount - 1))) {
            // Nothing to do, no ramp down duration configured or composition already ends at zero.
            return repeatIndex;
        }

        // We know the last segment is a ramp that ends at non-zero amplitude.
        RampSegment lastRamp = (RampSegment) segments.get(segmentCount - 1);
        float previousAmplitude = lastRamp.getEndAmplitude();
        float previousFrequency = lastRamp.getEndFrequency();

        if (isOffRampSegment(segments.get(repeatIndex))) {
            // Repeating from a non-zero to a zero amplitude segment, we know the next segment is a
            // ramp with zero amplitudes.
            RampSegment nextRamp = (RampSegment) segments.get(repeatIndex);

            if (nextRamp.getDuration() <= mRampDownDuration) {
                // Skip the zero amplitude segment and append a ramp down of same duration to the
                // end of the composition, to preserve waveform timings and still soften the
                // transition to zero.
                // This will update the waveform as follows:
                //  R               R+1
                //  |  ____          | ____
                // _|_/       =>   __|/    \
                segments.add(createRampDown(previousAmplitude, previousFrequency,
                        nextRamp.getDuration()));
                repeatIndex++;
            } else {
                // Append a ramp down to the end of the composition, split the zero amplitude
                // segment and start repeating from the second half, to preserve waveform timings.
                // This will update the waveform as follows:
                //  R              R+1
                //  |   ____        |  ____
                // _|__/       => __|_/    \
                segments.add(createRampDown(previousAmplitude, previousFrequency,
                        mRampDownDuration));
                segments.set(repeatIndex, updateDuration(nextRamp,
                        nextRamp.getDuration() - mRampDownDuration));
                segments.add(repeatIndex, updateDuration(nextRamp, mRampDownDuration));
                repeatIndex++;
            }
        }

        return repeatIndex;
    }

    private static RampSegment apply(StepSegment segment) {
        return new RampSegment(segment.getAmplitude(), segment.getAmplitude(),
        return new RampSegment(segment.getAmplitude(), segment.getAmplitude(),
                segment.getFrequency(), segment.getFrequency(), (int) segment.getDuration());
                segment.getFrequency(), segment.getFrequency(), (int) segment.getDuration());
    }
    }

    private static RampSegment createRampDown(float amplitude, float frequency, long duration) {
        return new RampSegment(amplitude, /* endAmplitude= */ 0, frequency, frequency,
                (int) duration);
    }

    private static RampSegment updateDuration(RampSegment ramp, long newDuration) {
        return new RampSegment(ramp.getStartAmplitude(), ramp.getEndAmplitude(),
                ramp.getStartFrequency(), ramp.getEndFrequency(), (int) newDuration);
    }

    private static boolean isStep(VibrationEffectSegment segment) {
        return segment instanceof StepSegment;
    }

    /** Returns true if the segment is a ramp that starts and ends at zero amplitude. */
    private static boolean isOffRampSegment(VibrationEffectSegment segment) {
        if (segment instanceof RampSegment) {
            RampSegment ramp = (RampSegment) segment;
            return ramp.getStartAmplitude() == 0 && ramp.getEndAmplitude() == 0;
        }
        return false;
    }

    private static boolean endsWithNonZeroAmplitude(VibrationEffectSegment segment) {
        if (segment instanceof RampSegment) {
            return ((RampSegment) segment).getEndAmplitude() != 0;
        }
        return false;
    }
}
}
Loading