Loading services/core/java/com/android/server/vibrator/DeviceVibrationEffectAdapter.java +4 −6 Original line number Diff line number Diff line Loading @@ -16,7 +16,6 @@ package com.android.server.vibrator; import android.content.Context; import android.os.VibrationEffect; import android.os.VibratorInfo; Loading @@ -29,14 +28,13 @@ final class DeviceVibrationEffectAdapter private final List<VibrationEffectAdapters.SegmentsAdapter<VibratorInfo>> mSegmentAdapters; DeviceVibrationEffectAdapter(Context context) { DeviceVibrationEffectAdapter(VibrationSettings settings) { mSegmentAdapters = Arrays.asList( // TODO(b/167947076): add filter that removes unsupported primitives // TODO(b/167947076): add filter that replaces unsupported prebaked with fallback new RampToStepAdapter(context.getResources().getInteger( com.android.internal.R.integer.config_vibrationWaveformRampStepDuration)), new StepToRampAdapter(context.getResources().getInteger( com.android.internal.R.integer.config_vibrationWaveformRampDownDuration)), new RampToStepAdapter(settings.getRampStepDuration()), new StepToRampAdapter(), new RampDownAdapter(settings.getRampDownDuration(), settings.getRampStepDuration()), new ClippingAmplitudeAndFrequencyAdapter() ); } Loading services/core/java/com/android/server/vibrator/RampDownAdapter.java 0 → 100644 +240 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.vibrator; import android.os.VibratorInfo; import android.os.vibrator.RampSegment; import android.os.vibrator.StepSegment; import android.os.vibrator.VibrationEffectSegment; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * Adapter that applies the ramp down duration config to bring down the vibrator amplitude smoothly. * * <p>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. The * effect overall duration is preserved by this transformation. * * <p>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 or step down will be added to the * transition between ON and OFF. The ramps/steps can be shorter than the configured duration in * order to preserve the waveform timings, but they will still soften the ringing effect. * * <p>If the segment preceding an OFF segment a {@link RampSegment} then a new ramp segment will be * added to bring the amplitude down. If it is a {@link StepSegment} then a sequence of steps will * be used to bring the amplitude down to zero. This ensures that the transition from the last * amplitude to zero will be handled by the same vibrate method. */ final class RampDownAdapter implements VibrationEffectAdapters.SegmentsAdapter<VibratorInfo> { private final int mRampDownDuration; private final int mStepDuration; RampDownAdapter(int rampDownDuration, int stepDuration) { mRampDownDuration = rampDownDuration; mStepDuration = stepDuration; } @Override public int apply(List<VibrationEffectSegment> segments, int repeatIndex, VibratorInfo info) { if (mRampDownDuration <= 0) { // Nothing to do, no ramp down duration configured. return repeatIndex; } repeatIndex = addRampDownToZeroAmplitudeSegments(segments, repeatIndex); repeatIndex = addRampDownToLoop(segments, repeatIndex); return repeatIndex; } /** * This will add ramp or steps down to zero as follows: * * <ol> * <li>Remove the OFF segment that follows a segment of non-zero amplitude; * <li>Add a single {@link RampSegment} or a list of {@link StepSegment} starting at the * previous segment's amplitude and frequency, with min between the configured ramp down * duration or the removed segment's duration; * <li>Add a zero amplitude segment following the steps, if necessary, to fill the remaining * duration; * </ol> */ private int addRampDownToZeroAmplitudeSegments(List<VibrationEffectSegment> segments, int repeatIndex) { int newRepeatIndex = repeatIndex; int newSegmentCount = segments.size(); for (int i = 1; i < newSegmentCount; i++) { VibrationEffectSegment previousSegment = segments.get(i - 1); if (!isOffSegment(segments.get(i)) || !endsWithNonZeroAmplitude(previousSegment)) { continue; } List<VibrationEffectSegment> replacementSegments = null; long offDuration = segments.get(i).getDuration(); if (previousSegment instanceof StepSegment) { float previousAmplitude = ((StepSegment) previousSegment).getAmplitude(); float previousFrequency = ((StepSegment) previousSegment).getFrequency(); replacementSegments = createStepsDown(previousAmplitude, previousFrequency, offDuration); } else if (previousSegment instanceof RampSegment) { float previousAmplitude = ((RampSegment) previousSegment).getEndAmplitude(); float previousFrequency = ((RampSegment) previousSegment).getEndFrequency(); if (offDuration <= mRampDownDuration) { // Replace the zero amplitude segment with a ramp down of same duration, to // preserve waveform timings and still soften the transition to zero. replacementSegments = Arrays.asList( createRampDown(previousAmplitude, previousFrequency, offDuration)); } else { // Replace the zero amplitude segment with a ramp down of configured duration // followed by a shorter off segment. replacementSegments = Arrays.asList( createRampDown(previousAmplitude, previousFrequency, mRampDownDuration), createRampDown(0, previousFrequency, offDuration - mRampDownDuration)); } } if (replacementSegments != null) { int segmentsAdded = replacementSegments.size() - 1; segments.remove(i); segments.addAll(i, replacementSegments); if (repeatIndex > i) { newRepeatIndex += segmentsAdded; } i += segmentsAdded; newSegmentCount += segmentsAdded; } } return newRepeatIndex; } /** * This will ramps down 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 has zero amplitude. * The update is described as: * * <ol> * <li>Add a ramp or sequence of steps 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) { // Nothing to do, no ramp down duration configured or effect is not repeating. return repeatIndex; } int segmentCount = segments.size(); if (!endsWithNonZeroAmplitude(segments.get(segmentCount - 1)) || !isOffSegment(segments.get(repeatIndex))) { // Nothing to do, not going back from a positive amplitude to a off segment. return repeatIndex; } VibrationEffectSegment lastSegment = segments.get(segmentCount - 1); VibrationEffectSegment offSegment = segments.get(repeatIndex); long offDuration = offSegment.getDuration(); if (offDuration > mRampDownDuration) { // 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.set(repeatIndex, updateDuration(offSegment, offDuration - mRampDownDuration)); segments.add(repeatIndex, updateDuration(offSegment, mRampDownDuration)); } // Skip the zero amplitude segment and append ramp/steps down at the end. repeatIndex++; if (lastSegment instanceof StepSegment) { float previousAmplitude = ((StepSegment) lastSegment).getAmplitude(); float previousFrequency = ((StepSegment) lastSegment).getFrequency(); segments.addAll(createStepsDown(previousAmplitude, previousFrequency, Math.min(offDuration, mRampDownDuration))); } else if (lastSegment instanceof RampSegment) { float previousAmplitude = ((RampSegment) lastSegment).getEndAmplitude(); float previousFrequency = ((RampSegment) lastSegment).getEndFrequency(); segments.add(createRampDown(previousAmplitude, previousFrequency, Math.min(offDuration, mRampDownDuration))); } return repeatIndex; } private List<VibrationEffectSegment> createStepsDown(float amplitude, float frequency, long duration) { // Step down for at most the configured ramp duration. int stepCount = (int) Math.min(duration, mRampDownDuration) / mStepDuration; float amplitudeStep = amplitude / stepCount; List<VibrationEffectSegment> steps = new ArrayList<>(); for (int i = 1; i < stepCount; i++) { steps.add(new StepSegment(amplitude - i * amplitudeStep, frequency, mStepDuration)); } int remainingDuration = (int) duration - mStepDuration * (stepCount - 1); steps.add(new StepSegment(0, frequency, remainingDuration)); return steps; } private static RampSegment createRampDown(float amplitude, float frequency, long duration) { return new RampSegment(amplitude, /* endAmplitude= */ 0, frequency, frequency, (int) duration); } private static VibrationEffectSegment updateDuration(VibrationEffectSegment segment, long newDuration) { if (segment instanceof RampSegment) { RampSegment ramp = (RampSegment) segment; return new RampSegment(ramp.getStartAmplitude(), ramp.getEndAmplitude(), ramp.getStartFrequency(), ramp.getEndFrequency(), (int) newDuration); } else if (segment instanceof StepSegment) { StepSegment step = (StepSegment) segment; return new StepSegment(step.getAmplitude(), step.getFrequency(), (int) newDuration); } return segment; } /** Returns true if the segment is a ramp or a step that starts and ends at zero amplitude. */ private static boolean isOffSegment(VibrationEffectSegment segment) { if (segment instanceof StepSegment) { StepSegment ramp = (StepSegment) segment; return ramp.getAmplitude() == 0; } else if (segment instanceof RampSegment) { RampSegment ramp = (RampSegment) segment; return ramp.getStartAmplitude() == 0 && ramp.getEndAmplitude() == 0; } return false; } /** Returns true if the segment is a ramp or a step that ends at a non-zero amplitude. */ private static boolean endsWithNonZeroAmplitude(VibrationEffectSegment segment) { if (segment instanceof StepSegment) { return ((StepSegment) segment).getAmplitude() != 0; } else if (segment instanceof RampSegment) { return ((RampSegment) segment).getEndAmplitude() != 0; } return false; } } services/core/java/com/android/server/vibrator/StepToRampAdapter.java +6 −175 Original line number Diff line number Diff line Loading @@ -31,25 +31,9 @@ import java.util.List; * <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> { private final int mRampDownDuration; StepToRampAdapter(int rampDownDuration) { mRampDownDuration = rampDownDuration; } @Override public int apply(List<VibrationEffectSegment> segments, int repeatIndex, VibratorInfo info) { Loading @@ -58,28 +42,17 @@ final class StepToRampAdapter implements VibrationEffectAdapters.SegmentsAdapter return repeatIndex; } convertStepsToRamps(segments); int newRepeatIndex = addRampDownToZeroAmplitudeSegments(segments, repeatIndex); newRepeatIndex = addRampDownToLoop(segments, newRepeatIndex); newRepeatIndex = splitLongRampSegments(info, segments, newRepeatIndex); return newRepeatIndex; repeatIndex = splitLongRampSegments(info, segments, repeatIndex); return repeatIndex; } private void convertStepsToRamps(List<VibrationEffectSegment> segments) { 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. for (int i = 0; i < segmentCount; i++) { VibrationEffectSegment segment = segments.get(i); if (isStep(segment) && ((StepSegment) segment).getFrequency() != 0) { segments.set(i, apply((StepSegment) segment)); segments.set(i, convertStepToRamp((StepSegment) segment)); } } // Convert steps that are next to ramps to also become ramps, so they can be composed Loading @@ -87,129 +60,13 @@ final class StepToRampAdapter implements VibrationEffectAdapters.SegmentsAdapter for (int i = 0; i < segmentCount; i++) { if (segments.get(i) instanceof RampSegment) { for (int j = i - 1; j >= 0 && isStep(segments.get(j)); j--) { segments.set(j, apply((StepSegment) segments.get(j))); segments.set(j, convertStepToRamp((StepSegment) segments.get(j))); } for (int j = i + 1; j < segmentCount && isStep(segments.get(j)); 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; } 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; segments.set(j, convertStepToRamp((StepSegment) segments.get(j))); } // 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; } /** * 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; } /** Loading Loading @@ -247,7 +104,7 @@ final class StepToRampAdapter implements VibrationEffectAdapters.SegmentsAdapter return repeatIndex; } private static RampSegment apply(StepSegment segment) { private static RampSegment convertStepToRamp(StepSegment segment) { return new RampSegment(segment.getAmplitude(), segment.getAmplitude(), segment.getFrequency(), segment.getFrequency(), (int) segment.getDuration()); } Loading Loading @@ -276,36 +133,10 @@ final class StepToRampAdapter implements VibrationEffectAdapters.SegmentsAdapter return ramps; } 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; } private static float interpolateAmplitude(RampSegment ramp, long duration) { return interpolate(ramp.getStartAmplitude(), ramp.getEndAmplitude(), duration, ramp.getDuration()); Loading services/core/java/com/android/server/vibrator/VibrationSettings.java +28 −1 File changed.Preview size limit exceeded, changes collapsed. Show changes services/core/java/com/android/server/vibrator/VibratorManagerService.java +1 −1 Original line number Diff line number Diff line Loading @@ -176,7 +176,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { mVibrationSettings = new VibrationSettings(mContext, mHandler); mVibrationScaler = new VibrationScaler(mContext, mVibrationSettings); mInputDeviceDelegate = new InputDeviceDelegate(mContext, mHandler); mDeviceVibrationEffectAdapter = new DeviceVibrationEffectAdapter(mContext); mDeviceVibrationEffectAdapter = new DeviceVibrationEffectAdapter(mVibrationSettings); VibrationCompleteListener listener = new VibrationCompleteListener(this); mNativeWrapper = injector.getNativeWrapper(); Loading Loading
services/core/java/com/android/server/vibrator/DeviceVibrationEffectAdapter.java +4 −6 Original line number Diff line number Diff line Loading @@ -16,7 +16,6 @@ package com.android.server.vibrator; import android.content.Context; import android.os.VibrationEffect; import android.os.VibratorInfo; Loading @@ -29,14 +28,13 @@ final class DeviceVibrationEffectAdapter private final List<VibrationEffectAdapters.SegmentsAdapter<VibratorInfo>> mSegmentAdapters; DeviceVibrationEffectAdapter(Context context) { DeviceVibrationEffectAdapter(VibrationSettings settings) { mSegmentAdapters = Arrays.asList( // TODO(b/167947076): add filter that removes unsupported primitives // TODO(b/167947076): add filter that replaces unsupported prebaked with fallback new RampToStepAdapter(context.getResources().getInteger( com.android.internal.R.integer.config_vibrationWaveformRampStepDuration)), new StepToRampAdapter(context.getResources().getInteger( com.android.internal.R.integer.config_vibrationWaveformRampDownDuration)), new RampToStepAdapter(settings.getRampStepDuration()), new StepToRampAdapter(), new RampDownAdapter(settings.getRampDownDuration(), settings.getRampStepDuration()), new ClippingAmplitudeAndFrequencyAdapter() ); } Loading
services/core/java/com/android/server/vibrator/RampDownAdapter.java 0 → 100644 +240 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.vibrator; import android.os.VibratorInfo; import android.os.vibrator.RampSegment; import android.os.vibrator.StepSegment; import android.os.vibrator.VibrationEffectSegment; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * Adapter that applies the ramp down duration config to bring down the vibrator amplitude smoothly. * * <p>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. The * effect overall duration is preserved by this transformation. * * <p>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 or step down will be added to the * transition between ON and OFF. The ramps/steps can be shorter than the configured duration in * order to preserve the waveform timings, but they will still soften the ringing effect. * * <p>If the segment preceding an OFF segment a {@link RampSegment} then a new ramp segment will be * added to bring the amplitude down. If it is a {@link StepSegment} then a sequence of steps will * be used to bring the amplitude down to zero. This ensures that the transition from the last * amplitude to zero will be handled by the same vibrate method. */ final class RampDownAdapter implements VibrationEffectAdapters.SegmentsAdapter<VibratorInfo> { private final int mRampDownDuration; private final int mStepDuration; RampDownAdapter(int rampDownDuration, int stepDuration) { mRampDownDuration = rampDownDuration; mStepDuration = stepDuration; } @Override public int apply(List<VibrationEffectSegment> segments, int repeatIndex, VibratorInfo info) { if (mRampDownDuration <= 0) { // Nothing to do, no ramp down duration configured. return repeatIndex; } repeatIndex = addRampDownToZeroAmplitudeSegments(segments, repeatIndex); repeatIndex = addRampDownToLoop(segments, repeatIndex); return repeatIndex; } /** * This will add ramp or steps down to zero as follows: * * <ol> * <li>Remove the OFF segment that follows a segment of non-zero amplitude; * <li>Add a single {@link RampSegment} or a list of {@link StepSegment} starting at the * previous segment's amplitude and frequency, with min between the configured ramp down * duration or the removed segment's duration; * <li>Add a zero amplitude segment following the steps, if necessary, to fill the remaining * duration; * </ol> */ private int addRampDownToZeroAmplitudeSegments(List<VibrationEffectSegment> segments, int repeatIndex) { int newRepeatIndex = repeatIndex; int newSegmentCount = segments.size(); for (int i = 1; i < newSegmentCount; i++) { VibrationEffectSegment previousSegment = segments.get(i - 1); if (!isOffSegment(segments.get(i)) || !endsWithNonZeroAmplitude(previousSegment)) { continue; } List<VibrationEffectSegment> replacementSegments = null; long offDuration = segments.get(i).getDuration(); if (previousSegment instanceof StepSegment) { float previousAmplitude = ((StepSegment) previousSegment).getAmplitude(); float previousFrequency = ((StepSegment) previousSegment).getFrequency(); replacementSegments = createStepsDown(previousAmplitude, previousFrequency, offDuration); } else if (previousSegment instanceof RampSegment) { float previousAmplitude = ((RampSegment) previousSegment).getEndAmplitude(); float previousFrequency = ((RampSegment) previousSegment).getEndFrequency(); if (offDuration <= mRampDownDuration) { // Replace the zero amplitude segment with a ramp down of same duration, to // preserve waveform timings and still soften the transition to zero. replacementSegments = Arrays.asList( createRampDown(previousAmplitude, previousFrequency, offDuration)); } else { // Replace the zero amplitude segment with a ramp down of configured duration // followed by a shorter off segment. replacementSegments = Arrays.asList( createRampDown(previousAmplitude, previousFrequency, mRampDownDuration), createRampDown(0, previousFrequency, offDuration - mRampDownDuration)); } } if (replacementSegments != null) { int segmentsAdded = replacementSegments.size() - 1; segments.remove(i); segments.addAll(i, replacementSegments); if (repeatIndex > i) { newRepeatIndex += segmentsAdded; } i += segmentsAdded; newSegmentCount += segmentsAdded; } } return newRepeatIndex; } /** * This will ramps down 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 has zero amplitude. * The update is described as: * * <ol> * <li>Add a ramp or sequence of steps 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) { // Nothing to do, no ramp down duration configured or effect is not repeating. return repeatIndex; } int segmentCount = segments.size(); if (!endsWithNonZeroAmplitude(segments.get(segmentCount - 1)) || !isOffSegment(segments.get(repeatIndex))) { // Nothing to do, not going back from a positive amplitude to a off segment. return repeatIndex; } VibrationEffectSegment lastSegment = segments.get(segmentCount - 1); VibrationEffectSegment offSegment = segments.get(repeatIndex); long offDuration = offSegment.getDuration(); if (offDuration > mRampDownDuration) { // 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.set(repeatIndex, updateDuration(offSegment, offDuration - mRampDownDuration)); segments.add(repeatIndex, updateDuration(offSegment, mRampDownDuration)); } // Skip the zero amplitude segment and append ramp/steps down at the end. repeatIndex++; if (lastSegment instanceof StepSegment) { float previousAmplitude = ((StepSegment) lastSegment).getAmplitude(); float previousFrequency = ((StepSegment) lastSegment).getFrequency(); segments.addAll(createStepsDown(previousAmplitude, previousFrequency, Math.min(offDuration, mRampDownDuration))); } else if (lastSegment instanceof RampSegment) { float previousAmplitude = ((RampSegment) lastSegment).getEndAmplitude(); float previousFrequency = ((RampSegment) lastSegment).getEndFrequency(); segments.add(createRampDown(previousAmplitude, previousFrequency, Math.min(offDuration, mRampDownDuration))); } return repeatIndex; } private List<VibrationEffectSegment> createStepsDown(float amplitude, float frequency, long duration) { // Step down for at most the configured ramp duration. int stepCount = (int) Math.min(duration, mRampDownDuration) / mStepDuration; float amplitudeStep = amplitude / stepCount; List<VibrationEffectSegment> steps = new ArrayList<>(); for (int i = 1; i < stepCount; i++) { steps.add(new StepSegment(amplitude - i * amplitudeStep, frequency, mStepDuration)); } int remainingDuration = (int) duration - mStepDuration * (stepCount - 1); steps.add(new StepSegment(0, frequency, remainingDuration)); return steps; } private static RampSegment createRampDown(float amplitude, float frequency, long duration) { return new RampSegment(amplitude, /* endAmplitude= */ 0, frequency, frequency, (int) duration); } private static VibrationEffectSegment updateDuration(VibrationEffectSegment segment, long newDuration) { if (segment instanceof RampSegment) { RampSegment ramp = (RampSegment) segment; return new RampSegment(ramp.getStartAmplitude(), ramp.getEndAmplitude(), ramp.getStartFrequency(), ramp.getEndFrequency(), (int) newDuration); } else if (segment instanceof StepSegment) { StepSegment step = (StepSegment) segment; return new StepSegment(step.getAmplitude(), step.getFrequency(), (int) newDuration); } return segment; } /** Returns true if the segment is a ramp or a step that starts and ends at zero amplitude. */ private static boolean isOffSegment(VibrationEffectSegment segment) { if (segment instanceof StepSegment) { StepSegment ramp = (StepSegment) segment; return ramp.getAmplitude() == 0; } else if (segment instanceof RampSegment) { RampSegment ramp = (RampSegment) segment; return ramp.getStartAmplitude() == 0 && ramp.getEndAmplitude() == 0; } return false; } /** Returns true if the segment is a ramp or a step that ends at a non-zero amplitude. */ private static boolean endsWithNonZeroAmplitude(VibrationEffectSegment segment) { if (segment instanceof StepSegment) { return ((StepSegment) segment).getAmplitude() != 0; } else if (segment instanceof RampSegment) { return ((RampSegment) segment).getEndAmplitude() != 0; } return false; } }
services/core/java/com/android/server/vibrator/StepToRampAdapter.java +6 −175 Original line number Diff line number Diff line Loading @@ -31,25 +31,9 @@ import java.util.List; * <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> { private final int mRampDownDuration; StepToRampAdapter(int rampDownDuration) { mRampDownDuration = rampDownDuration; } @Override public int apply(List<VibrationEffectSegment> segments, int repeatIndex, VibratorInfo info) { Loading @@ -58,28 +42,17 @@ final class StepToRampAdapter implements VibrationEffectAdapters.SegmentsAdapter return repeatIndex; } convertStepsToRamps(segments); int newRepeatIndex = addRampDownToZeroAmplitudeSegments(segments, repeatIndex); newRepeatIndex = addRampDownToLoop(segments, newRepeatIndex); newRepeatIndex = splitLongRampSegments(info, segments, newRepeatIndex); return newRepeatIndex; repeatIndex = splitLongRampSegments(info, segments, repeatIndex); return repeatIndex; } private void convertStepsToRamps(List<VibrationEffectSegment> segments) { 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. for (int i = 0; i < segmentCount; i++) { VibrationEffectSegment segment = segments.get(i); if (isStep(segment) && ((StepSegment) segment).getFrequency() != 0) { segments.set(i, apply((StepSegment) segment)); segments.set(i, convertStepToRamp((StepSegment) segment)); } } // Convert steps that are next to ramps to also become ramps, so they can be composed Loading @@ -87,129 +60,13 @@ final class StepToRampAdapter implements VibrationEffectAdapters.SegmentsAdapter for (int i = 0; i < segmentCount; i++) { if (segments.get(i) instanceof RampSegment) { for (int j = i - 1; j >= 0 && isStep(segments.get(j)); j--) { segments.set(j, apply((StepSegment) segments.get(j))); segments.set(j, convertStepToRamp((StepSegment) segments.get(j))); } for (int j = i + 1; j < segmentCount && isStep(segments.get(j)); 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; } 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; segments.set(j, convertStepToRamp((StepSegment) segments.get(j))); } // 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; } /** * 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; } /** Loading Loading @@ -247,7 +104,7 @@ final class StepToRampAdapter implements VibrationEffectAdapters.SegmentsAdapter return repeatIndex; } private static RampSegment apply(StepSegment segment) { private static RampSegment convertStepToRamp(StepSegment segment) { return new RampSegment(segment.getAmplitude(), segment.getAmplitude(), segment.getFrequency(), segment.getFrequency(), (int) segment.getDuration()); } Loading Loading @@ -276,36 +133,10 @@ final class StepToRampAdapter implements VibrationEffectAdapters.SegmentsAdapter return ramps; } 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; } private static float interpolateAmplitude(RampSegment ramp, long duration) { return interpolate(ramp.getStartAmplitude(), ramp.getEndAmplitude(), duration, ramp.getDuration()); Loading
services/core/java/com/android/server/vibrator/VibrationSettings.java +28 −1 File changed.Preview size limit exceeded, changes collapsed. Show changes
services/core/java/com/android/server/vibrator/VibratorManagerService.java +1 −1 Original line number Diff line number Diff line Loading @@ -176,7 +176,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { mVibrationSettings = new VibrationSettings(mContext, mHandler); mVibrationScaler = new VibrationScaler(mContext, mVibrationSettings); mInputDeviceDelegate = new InputDeviceDelegate(mContext, mHandler); mDeviceVibrationEffectAdapter = new DeviceVibrationEffectAdapter(mContext); mDeviceVibrationEffectAdapter = new DeviceVibrationEffectAdapter(mVibrationSettings); VibrationCompleteListener listener = new VibrationCompleteListener(this); mNativeWrapper = injector.getNativeWrapper(); Loading