Merge "Allow upcoming vibration when current one if being cancelled" into tm-qpr-dev

This commit is contained in:
Lais Andrade
2022-10-20 15:46:12 +00:00
committed by Android (Google) Code Review
5 changed files with 75 additions and 18 deletions

View File

@@ -65,6 +65,9 @@ final class VibrationStepConductor implements IBinder.DeathRecipient {
public final DeviceVibrationEffectAdapter deviceEffectAdapter;
public final VibrationThread.VibratorManagerHooks vibratorManagerHooks;
// Not guarded by lock because they're not modified by this conductor, it's used here only to
// check immutable attributes. The status and other mutable states are changed by the service or
// by the vibrator steps.
private final Vibration mVibration;
private final SparseArray<VibratorController> mVibrators = new SparseArray<>();
@@ -405,6 +408,16 @@ final class VibrationStepConductor implements IBinder.DeathRecipient {
}
}
/** Returns true if a cancellation signal was sent via {@link #notifyCancelled}. */
public boolean wasNotifiedToCancel() {
if (Build.IS_DEBUGGABLE) {
expectIsVibrationThread(false);
}
synchronized (mLock) {
return mSignalCancel != null;
}
}
@GuardedBy("mLock")
private boolean hasPendingNotifySignalLocked() {
if (Build.IS_DEBUGGABLE) {

View File

@@ -852,8 +852,8 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
Vibration currentVibration = mCurrentVibration.getVibration();
if (currentVibration.hasEnded()) {
// Current vibration is finishing up, it should not block incoming vibrations.
if (currentVibration.hasEnded() || mCurrentVibration.wasNotifiedToCancel()) {
// Current vibration has ended or is cancelling, should not block incoming vibrations.
return null;
}

View File

@@ -53,7 +53,8 @@ final class FakeVibratorControllerProvider {
private boolean mIsAvailable = true;
private boolean mIsInfoLoadSuccessful = true;
private long mLatency;
private long mOnLatency;
private long mOffLatency;
private int mOffCount;
private int mCapabilities;
@@ -97,7 +98,7 @@ final class FakeVibratorControllerProvider {
public long on(long milliseconds, long vibrationId) {
recordEffectSegment(vibrationId, new StepSegment(VibrationEffect.DEFAULT_AMPLITUDE,
/* frequencyHz= */ 0, (int) milliseconds));
applyLatency();
applyLatency(mOnLatency);
scheduleListener(milliseconds, vibrationId);
return milliseconds;
}
@@ -105,12 +106,13 @@ final class FakeVibratorControllerProvider {
@Override
public void off() {
mOffCount++;
applyLatency(mOffLatency);
}
@Override
public void setAmplitude(float amplitude) {
mAmplitudes.add(amplitude);
applyLatency();
applyLatency(mOnLatency);
}
@Override
@@ -121,7 +123,7 @@ final class FakeVibratorControllerProvider {
}
recordEffectSegment(vibrationId,
new PrebakedSegment((int) effect, false, (int) strength));
applyLatency();
applyLatency(mOnLatency);
scheduleListener(EFFECT_DURATION, vibrationId);
return EFFECT_DURATION;
}
@@ -141,7 +143,7 @@ final class FakeVibratorControllerProvider {
duration += EFFECT_DURATION + primitive.getDelay();
recordEffectSegment(vibrationId, primitive);
}
applyLatency();
applyLatency(mOnLatency);
scheduleListener(duration, vibrationId);
return duration;
}
@@ -154,7 +156,7 @@ final class FakeVibratorControllerProvider {
recordEffectSegment(vibrationId, primitive);
}
recordBraking(vibrationId, braking);
applyLatency();
applyLatency(mOnLatency);
scheduleListener(duration, vibrationId);
return duration;
}
@@ -193,10 +195,10 @@ final class FakeVibratorControllerProvider {
return mIsInfoLoadSuccessful;
}
private void applyLatency() {
private void applyLatency(long latencyMillis) {
try {
if (mLatency > 0) {
Thread.sleep(mLatency);
if (latencyMillis > 0) {
Thread.sleep(latencyMillis);
}
} catch (InterruptedException e) {
}
@@ -240,10 +242,15 @@ final class FakeVibratorControllerProvider {
/**
* Sets the latency this controller should fake for turning the vibrator hardware on or setting
* it's vibration amplitude.
* the vibration amplitude.
*/
public void setLatency(long millis) {
mLatency = millis;
public void setOnLatency(long millis) {
mOnLatency = millis;
}
/** Sets the latency this controller should fake for turning the vibrator off. */
public void setOffLatency(long millis) {
mOffLatency = millis;
}
/** Set the capabilities of the fake vibrator hardware. */

View File

@@ -1118,7 +1118,7 @@ public class VibrationThreadTest {
// 25% of the first waveform step will be spent on the native on() call.
// 25% of each waveform step will be spent on the native setAmplitude() call..
mVibratorProviders.get(VIBRATOR_ID).setLatency(stepDuration / 4);
mVibratorProviders.get(VIBRATOR_ID).setOnLatency(stepDuration / 4);
mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
int stepCount = totalDuration / stepDuration;
@@ -1149,7 +1149,7 @@ public class VibrationThreadTest {
fakeVibrator.setSupportedEffects(VibrationEffect.EFFECT_CLICK);
long latency = 5_000; // 5s
fakeVibrator.setLatency(latency);
fakeVibrator.setOnLatency(latency);
long vibrationId = 1;
VibrationEffect effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
@@ -1163,8 +1163,7 @@ public class VibrationThreadTest {
// fail at waitForCompletion(cancellingThread).
Thread cancellingThread = new Thread(
() -> conductor.notifyCancelled(
new Vibration.EndInfo(
Vibration.Status.CANCELLED_BY_USER),
new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_USER),
/* immediate= */ false));
cancellingThread.start();

View File

@@ -817,6 +817,39 @@ public class VibratorManagerServiceTest {
verify(mBatteryStatsMock).noteVibratorOn(UID, 5000);
// The second vibration shouldn't have recorded that the vibrators were turned on.
verify(mBatteryStatsMock, times(1)).noteVibratorOn(anyInt(), anyLong());
// No segment played is the prebaked CLICK from the second vibration.
assertFalse(mVibratorProviders.get(1).getAllEffectSegments().stream()
.anyMatch(PrebakedSegment.class::isInstance));
// Clean up repeating effect.
service.cancelVibrate(VibrationAttributes.USAGE_FILTER_MATCH_ALL, service);
}
@Test
public void vibrate_withOngoingRepeatingVibrationBeingCancelled_playsAfterPreviousIsCancelled()
throws Exception {
mockVibrators(1);
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1);
fakeVibrator.setOffLatency(50); // Add latency so cancellation is slow.
fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
fakeVibrator.setSupportedEffects(VibrationEffect.EFFECT_CLICK);
VibratorManagerService service = createSystemReadyService();
VibrationEffect repeatingEffect = VibrationEffect.createWaveform(
new long[]{10, 10_000}, new int[]{255, 0}, 1);
vibrate(service, repeatingEffect, ALARM_ATTRS);
// VibrationThread will start this vibration async, wait until the off waveform step.
assertTrue(waitUntil(s -> fakeVibrator.getOffCount() > 0, service, TEST_TIMEOUT_MILLIS));
// Cancel vibration right before requesting a new one.
// This should trigger slow IVibrator.off before setting the vibration status to cancelled.
service.cancelVibrate(VibrationAttributes.USAGE_FILTER_MATCH_ALL, service);
vibrateAndWaitUntilFinished(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK),
ALARM_ATTRS);
// Check that second vibration was played.
assertTrue(fakeVibrator.getAllEffectSegments().stream()
.anyMatch(PrebakedSegment.class::isInstance));
}
@Test
@@ -867,6 +900,11 @@ public class VibratorManagerServiceTest {
// The second vibration shouldn't have recorded that the vibrators were turned on.
verify(mBatteryStatsMock, times(1)).noteVibratorOn(anyInt(), anyLong());
// The second vibration shouldn't have played any prebaked segment.
assertFalse(mVibratorProviders.get(1).getAllEffectSegments().stream()
.anyMatch(PrebakedSegment.class::isInstance));
// Clean up long effect.
service.cancelVibrate(VibrationAttributes.USAGE_FILTER_MATCH_ALL, service);
}
@Test