Merge "Allow upcoming vibration when current one if being cancelled" into tm-qpr-dev
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user