Merge "Don't take the conductor lock for syncing vibrations." into tm-dev

This commit is contained in:
Simon Bowden
2022-02-22 14:47:15 +00:00
committed by Android (Google) Code Review
2 changed files with 36 additions and 37 deletions

View File

@@ -189,44 +189,42 @@ final class StartSequentialEffectStep extends Step {
// vibrating different sets of vibrators in parallel. The manager can only prepareSynced
// one set of vibrators at a time.
// This property is guaranteed by there only being one thread (VibrationThread) executing
// one Step at a time, so there's no need to hold the state lock.
// TODO: remove the large locked block in a dedicated change.
synchronized (conductor.mLock) {
boolean hasPrepared = false;
boolean hasTriggered = false;
long maxDuration = 0;
try {
hasPrepared = conductor.vibratorManagerHooks.prepareSyncedVibration(
effectMapping.getRequiredSyncCapabilities(),
effectMapping.getVibratorIds());
// one Step at a time, so there's no need to hold the state lock. Callbacks will be
// delivered asynchronously but enqueued until the step processing is finished.
boolean hasPrepared = false;
boolean hasTriggered = false;
long maxDuration = 0;
try {
hasPrepared = conductor.vibratorManagerHooks.prepareSyncedVibration(
effectMapping.getRequiredSyncCapabilities(),
effectMapping.getVibratorIds());
for (AbstractVibratorStep step : steps) {
long duration = startVibrating(step, nextSteps);
if (duration < 0) {
// One vibrator has failed, fail this entire sync attempt.
return maxDuration = -1;
}
maxDuration = Math.max(maxDuration, duration);
for (AbstractVibratorStep step : steps) {
long duration = startVibrating(step, nextSteps);
if (duration < 0) {
// One vibrator has failed, fail this entire sync attempt.
return maxDuration = -1;
}
maxDuration = Math.max(maxDuration, duration);
}
// Check if sync was prepared and if any step was accepted by a vibrator,
// otherwise there is nothing to trigger here.
if (hasPrepared && maxDuration > 0) {
hasTriggered = conductor.vibratorManagerHooks.triggerSyncedVibration(
getVibration().id);
}
return maxDuration;
} finally {
if (hasPrepared && !hasTriggered) {
// Trigger has failed or all steps were ignored by the vibrators.
conductor.vibratorManagerHooks.cancelSyncedVibration();
nextSteps.clear();
} else if (maxDuration < 0) {
// Some vibrator failed without being prepared so other vibrators might be
// active. Cancel and remove every pending step from output list.
for (int i = nextSteps.size() - 1; i >= 0; i--) {
nextSteps.remove(i).cancelImmediately();
}
// Check if sync was prepared and if any step was accepted by a vibrator,
// otherwise there is nothing to trigger here.
if (hasPrepared && maxDuration > 0) {
hasTriggered = conductor.vibratorManagerHooks.triggerSyncedVibration(
getVibration().id);
}
return maxDuration;
} finally {
if (hasPrepared && !hasTriggered) {
// Trigger has failed or all steps were ignored by the vibrators.
conductor.vibratorManagerHooks.cancelSyncedVibration();
nextSteps.clear();
} else if (maxDuration < 0) {
// Some vibrator failed without being prepared so other vibrators might be
// active. Cancel and remove every pending step from output list.
for (int i = nextSteps.size() - 1; i >= 0; i--) {
nextSteps.remove(i).cancelImmediately();
}
}
}

View File

@@ -242,8 +242,9 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
}
}
// Only run the next vibration step if we didn't have to wait in this loop.
// If we waited then the queue may have changed, so loop again to re-evaluate
// the scheduling of the queue top element.
// If we waited then the queue may have changed or the wait could have been
// interrupted by a cancel call, so loop again to re-evaluate the scheduling of
// the queue top element.
if (waitMillisBeforeNextStep <= 0) {
if (DEBUG) {
Slog.d(TAG, "Play vibration consuming next step...");