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 DeviceVibrationEffectAdapter deviceEffectAdapter;
|
||||||
public final VibrationThread.VibratorManagerHooks vibratorManagerHooks;
|
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 Vibration mVibration;
|
||||||
private final SparseArray<VibratorController> mVibrators = new SparseArray<>();
|
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")
|
@GuardedBy("mLock")
|
||||||
private boolean hasPendingNotifySignalLocked() {
|
private boolean hasPendingNotifySignalLocked() {
|
||||||
if (Build.IS_DEBUGGABLE) {
|
if (Build.IS_DEBUGGABLE) {
|
||||||
|
|||||||
@@ -852,8 +852,8 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Vibration currentVibration = mCurrentVibration.getVibration();
|
Vibration currentVibration = mCurrentVibration.getVibration();
|
||||||
if (currentVibration.hasEnded()) {
|
if (currentVibration.hasEnded() || mCurrentVibration.wasNotifiedToCancel()) {
|
||||||
// Current vibration is finishing up, it should not block incoming vibrations.
|
// Current vibration has ended or is cancelling, should not block incoming vibrations.
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -53,7 +53,8 @@ final class FakeVibratorControllerProvider {
|
|||||||
|
|
||||||
private boolean mIsAvailable = true;
|
private boolean mIsAvailable = true;
|
||||||
private boolean mIsInfoLoadSuccessful = true;
|
private boolean mIsInfoLoadSuccessful = true;
|
||||||
private long mLatency;
|
private long mOnLatency;
|
||||||
|
private long mOffLatency;
|
||||||
private int mOffCount;
|
private int mOffCount;
|
||||||
|
|
||||||
private int mCapabilities;
|
private int mCapabilities;
|
||||||
@@ -97,7 +98,7 @@ final class FakeVibratorControllerProvider {
|
|||||||
public long on(long milliseconds, long vibrationId) {
|
public long on(long milliseconds, long vibrationId) {
|
||||||
recordEffectSegment(vibrationId, new StepSegment(VibrationEffect.DEFAULT_AMPLITUDE,
|
recordEffectSegment(vibrationId, new StepSegment(VibrationEffect.DEFAULT_AMPLITUDE,
|
||||||
/* frequencyHz= */ 0, (int) milliseconds));
|
/* frequencyHz= */ 0, (int) milliseconds));
|
||||||
applyLatency();
|
applyLatency(mOnLatency);
|
||||||
scheduleListener(milliseconds, vibrationId);
|
scheduleListener(milliseconds, vibrationId);
|
||||||
return milliseconds;
|
return milliseconds;
|
||||||
}
|
}
|
||||||
@@ -105,12 +106,13 @@ final class FakeVibratorControllerProvider {
|
|||||||
@Override
|
@Override
|
||||||
public void off() {
|
public void off() {
|
||||||
mOffCount++;
|
mOffCount++;
|
||||||
|
applyLatency(mOffLatency);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setAmplitude(float amplitude) {
|
public void setAmplitude(float amplitude) {
|
||||||
mAmplitudes.add(amplitude);
|
mAmplitudes.add(amplitude);
|
||||||
applyLatency();
|
applyLatency(mOnLatency);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -121,7 +123,7 @@ final class FakeVibratorControllerProvider {
|
|||||||
}
|
}
|
||||||
recordEffectSegment(vibrationId,
|
recordEffectSegment(vibrationId,
|
||||||
new PrebakedSegment((int) effect, false, (int) strength));
|
new PrebakedSegment((int) effect, false, (int) strength));
|
||||||
applyLatency();
|
applyLatency(mOnLatency);
|
||||||
scheduleListener(EFFECT_DURATION, vibrationId);
|
scheduleListener(EFFECT_DURATION, vibrationId);
|
||||||
return EFFECT_DURATION;
|
return EFFECT_DURATION;
|
||||||
}
|
}
|
||||||
@@ -141,7 +143,7 @@ final class FakeVibratorControllerProvider {
|
|||||||
duration += EFFECT_DURATION + primitive.getDelay();
|
duration += EFFECT_DURATION + primitive.getDelay();
|
||||||
recordEffectSegment(vibrationId, primitive);
|
recordEffectSegment(vibrationId, primitive);
|
||||||
}
|
}
|
||||||
applyLatency();
|
applyLatency(mOnLatency);
|
||||||
scheduleListener(duration, vibrationId);
|
scheduleListener(duration, vibrationId);
|
||||||
return duration;
|
return duration;
|
||||||
}
|
}
|
||||||
@@ -154,7 +156,7 @@ final class FakeVibratorControllerProvider {
|
|||||||
recordEffectSegment(vibrationId, primitive);
|
recordEffectSegment(vibrationId, primitive);
|
||||||
}
|
}
|
||||||
recordBraking(vibrationId, braking);
|
recordBraking(vibrationId, braking);
|
||||||
applyLatency();
|
applyLatency(mOnLatency);
|
||||||
scheduleListener(duration, vibrationId);
|
scheduleListener(duration, vibrationId);
|
||||||
return duration;
|
return duration;
|
||||||
}
|
}
|
||||||
@@ -193,10 +195,10 @@ final class FakeVibratorControllerProvider {
|
|||||||
return mIsInfoLoadSuccessful;
|
return mIsInfoLoadSuccessful;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void applyLatency() {
|
private void applyLatency(long latencyMillis) {
|
||||||
try {
|
try {
|
||||||
if (mLatency > 0) {
|
if (latencyMillis > 0) {
|
||||||
Thread.sleep(mLatency);
|
Thread.sleep(latencyMillis);
|
||||||
}
|
}
|
||||||
} catch (InterruptedException e) {
|
} 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
|
* 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) {
|
public void setOnLatency(long millis) {
|
||||||
mLatency = 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. */
|
/** 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 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..
|
// 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);
|
mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
|
||||||
|
|
||||||
int stepCount = totalDuration / stepDuration;
|
int stepCount = totalDuration / stepDuration;
|
||||||
@@ -1149,7 +1149,7 @@ public class VibrationThreadTest {
|
|||||||
fakeVibrator.setSupportedEffects(VibrationEffect.EFFECT_CLICK);
|
fakeVibrator.setSupportedEffects(VibrationEffect.EFFECT_CLICK);
|
||||||
|
|
||||||
long latency = 5_000; // 5s
|
long latency = 5_000; // 5s
|
||||||
fakeVibrator.setLatency(latency);
|
fakeVibrator.setOnLatency(latency);
|
||||||
|
|
||||||
long vibrationId = 1;
|
long vibrationId = 1;
|
||||||
VibrationEffect effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
|
VibrationEffect effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
|
||||||
@@ -1163,8 +1163,7 @@ public class VibrationThreadTest {
|
|||||||
// fail at waitForCompletion(cancellingThread).
|
// fail at waitForCompletion(cancellingThread).
|
||||||
Thread cancellingThread = new Thread(
|
Thread cancellingThread = new Thread(
|
||||||
() -> conductor.notifyCancelled(
|
() -> conductor.notifyCancelled(
|
||||||
new Vibration.EndInfo(
|
new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_USER),
|
||||||
Vibration.Status.CANCELLED_BY_USER),
|
|
||||||
/* immediate= */ false));
|
/* immediate= */ false));
|
||||||
cancellingThread.start();
|
cancellingThread.start();
|
||||||
|
|
||||||
|
|||||||
@@ -817,6 +817,39 @@ public class VibratorManagerServiceTest {
|
|||||||
verify(mBatteryStatsMock).noteVibratorOn(UID, 5000);
|
verify(mBatteryStatsMock).noteVibratorOn(UID, 5000);
|
||||||
// The second vibration shouldn't have recorded that the vibrators were turned on.
|
// The second vibration shouldn't have recorded that the vibrators were turned on.
|
||||||
verify(mBatteryStatsMock, times(1)).noteVibratorOn(anyInt(), anyLong());
|
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
|
@Test
|
||||||
@@ -867,6 +900,11 @@ public class VibratorManagerServiceTest {
|
|||||||
|
|
||||||
// The second vibration shouldn't have recorded that the vibrators were turned on.
|
// The second vibration shouldn't have recorded that the vibrators were turned on.
|
||||||
verify(mBatteryStatsMock, times(1)).noteVibratorOn(anyInt(), anyLong());
|
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
|
@Test
|
||||||
|
|||||||
Reference in New Issue
Block a user