Merge "Cancel current vibration asynchronously in VibratorService" into sc-dev am: 772414320c
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/13415167 MUST ONLY BE SUBMITTED BY AUTOMERGER Change-Id: I225b17f5a1e37bf899d98ef01570f89929e4924f
This commit is contained in:
@@ -102,7 +102,10 @@ public class VibratorService extends IVibratorService.Stub {
|
|||||||
private VibrationScaler mVibrationScaler;
|
private VibrationScaler mVibrationScaler;
|
||||||
private InputDeviceDelegate mInputDeviceDelegate;
|
private InputDeviceDelegate mInputDeviceDelegate;
|
||||||
|
|
||||||
private volatile VibrationThread mThread;
|
@GuardedBy("mLock")
|
||||||
|
private VibrationThread mThread;
|
||||||
|
@GuardedBy("mLock")
|
||||||
|
private VibrationThread mNextVibrationThread;
|
||||||
|
|
||||||
@GuardedBy("mLock")
|
@GuardedBy("mLock")
|
||||||
private Vibration mCurrentVibration;
|
private Vibration mCurrentVibration;
|
||||||
@@ -132,6 +135,10 @@ public class VibratorService extends IVibratorService.Stub {
|
|||||||
if (mCurrentVibration != null && mCurrentVibration.id == vibrationId) {
|
if (mCurrentVibration != null && mCurrentVibration.id == vibrationId) {
|
||||||
mThread = null;
|
mThread = null;
|
||||||
reportFinishVibrationLocked(status);
|
reportFinishVibrationLocked(status);
|
||||||
|
if (mNextVibrationThread != null) {
|
||||||
|
startVibrationThreadLocked(mNextVibrationThread);
|
||||||
|
mNextVibrationThread = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -258,18 +265,14 @@ public class VibratorService extends IVibratorService.Stub {
|
|||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public void onVibrationComplete(int vibratorId, long vibrationId) {
|
public void onVibrationComplete(int vibratorId, long vibrationId) {
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
if (mCurrentVibration != null && mCurrentVibration.id == vibrationId) {
|
if (mCurrentVibration != null && mCurrentVibration.id == vibrationId
|
||||||
|
&& mThread != null) {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Slog.d(TAG, "Vibration onComplete callback, notifying VibrationThread");
|
Slog.d(TAG, "Vibration onComplete callback, notifying VibrationThread");
|
||||||
}
|
}
|
||||||
if (mThread != null) {
|
|
||||||
// Let the thread playing the vibration handle the callback, since it might be
|
// Let the thread playing the vibration handle the callback, since it might be
|
||||||
// expecting the vibrator to turn off multiple times during a single vibration.
|
// expecting the vibrator to turn off multiple times during a single vibration.
|
||||||
mThread.vibratorComplete(vibratorId);
|
mThread.vibratorComplete(vibratorId);
|
||||||
} else {
|
|
||||||
// No vibration is playing in the thread, but clean up service just in case.
|
|
||||||
doCancelVibrateLocked(Vibration.Status.FINISHED);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -462,8 +465,10 @@ public class VibratorService extends IVibratorService.Stub {
|
|||||||
try {
|
try {
|
||||||
doCancelVibrateLocked(Vibration.Status.CANCELLED);
|
doCancelVibrateLocked(Vibration.Status.CANCELLED);
|
||||||
startVibrationLocked(vib);
|
startVibrationLocked(vib);
|
||||||
|
boolean isNextVibration = mNextVibrationThread != null
|
||||||
|
&& vib.equals(mNextVibrationThread.getVibration());
|
||||||
|
|
||||||
if (!vib.hasEnded() && mCurrentVibration.id != vib.id) {
|
if (!vib.hasEnded() && !vib.equals(mCurrentVibration) && !isNextVibration) {
|
||||||
// Vibration was unexpectedly ignored: add to list for debugging
|
// Vibration was unexpectedly ignored: add to list for debugging
|
||||||
endVibrationLocked(vib, Vibration.Status.IGNORED);
|
endVibrationLocked(vib, Vibration.Status.IGNORED);
|
||||||
}
|
}
|
||||||
@@ -532,6 +537,7 @@ public class VibratorService extends IVibratorService.Stub {
|
|||||||
}
|
}
|
||||||
final long ident = Binder.clearCallingIdentity();
|
final long ident = Binder.clearCallingIdentity();
|
||||||
try {
|
try {
|
||||||
|
mNextVibrationThread = null;
|
||||||
doCancelVibrateLocked(Vibration.Status.CANCELLED);
|
doCancelVibrateLocked(Vibration.Status.CANCELLED);
|
||||||
} finally {
|
} finally {
|
||||||
Binder.restoreCallingIdentity(ident);
|
Binder.restoreCallingIdentity(ident);
|
||||||
@@ -546,16 +552,14 @@ public class VibratorService extends IVibratorService.Stub {
|
|||||||
try {
|
try {
|
||||||
if (mThread != null) {
|
if (mThread != null) {
|
||||||
mThread.cancel();
|
mThread.cancel();
|
||||||
mThread = null;
|
|
||||||
}
|
}
|
||||||
|
mInputDeviceDelegate.cancelVibrateIfAvailable();
|
||||||
if (mCurrentExternalVibration != null) {
|
if (mCurrentExternalVibration != null) {
|
||||||
endVibrationLocked(mCurrentExternalVibration, status);
|
endVibrationLocked(mCurrentExternalVibration, status);
|
||||||
mCurrentExternalVibration.externalVibration.mute();
|
mCurrentExternalVibration.externalVibration.mute();
|
||||||
mCurrentExternalVibration = null;
|
mCurrentExternalVibration = null;
|
||||||
mVibratorController.setExternalControl(false);
|
mVibratorController.setExternalControl(false);
|
||||||
}
|
}
|
||||||
doVibratorOff();
|
|
||||||
reportFinishVibrationLocked(status);
|
|
||||||
} finally {
|
} finally {
|
||||||
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
|
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
|
||||||
}
|
}
|
||||||
@@ -579,28 +583,30 @@ public class VibratorService extends IVibratorService.Stub {
|
|||||||
private void startVibrationInnerLocked(Vibration vib) {
|
private void startVibrationInnerLocked(Vibration vib) {
|
||||||
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "startVibrationInnerLocked");
|
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "startVibrationInnerLocked");
|
||||||
try {
|
try {
|
||||||
// Set current vibration before starting it, so callback will work.
|
|
||||||
mCurrentVibration = vib;
|
|
||||||
VibrationEffect effect = getEffect(vib);
|
|
||||||
Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
|
|
||||||
boolean inputDevicesAvailable = mInputDeviceDelegate.vibrateIfAvailable(
|
boolean inputDevicesAvailable = mInputDeviceDelegate.vibrateIfAvailable(
|
||||||
vib.uid, vib.opPkg, vib.getEffect(), vib.reason, vib.attrs);
|
vib.uid, vib.opPkg, vib.getEffect(), vib.reason, vib.attrs);
|
||||||
if (inputDevicesAvailable) {
|
if (inputDevicesAvailable) {
|
||||||
// The set current vibration is no longer being played by this service, so drop it.
|
|
||||||
mCurrentVibration = null;
|
|
||||||
endVibrationLocked(vib, Vibration.Status.FORWARDED_TO_INPUT_DEVICES);
|
endVibrationLocked(vib, Vibration.Status.FORWARDED_TO_INPUT_DEVICES);
|
||||||
|
} else if (mThread == null) {
|
||||||
|
startVibrationThreadLocked(new VibrationThread(vib, mVibratorController, mWakeLock,
|
||||||
|
mBatteryStatsService, mVibrationCallbacks));
|
||||||
} else {
|
} else {
|
||||||
// mThread better be null here. doCancelVibrate should always be
|
mNextVibrationThread = new VibrationThread(vib, mVibratorController, mWakeLock,
|
||||||
// called before startVibrationInnerLocked
|
|
||||||
mThread = new VibrationThread(vib, mVibratorController, mWakeLock,
|
|
||||||
mBatteryStatsService, mVibrationCallbacks);
|
mBatteryStatsService, mVibrationCallbacks);
|
||||||
mThread.start();
|
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
|
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GuardedBy("mLock")
|
||||||
|
private void startVibrationThreadLocked(VibrationThread thread) {
|
||||||
|
Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
|
||||||
|
mCurrentVibration = thread.getVibration();
|
||||||
|
mThread = thread;
|
||||||
|
mThread.start();
|
||||||
|
}
|
||||||
|
|
||||||
/** Scale the vibration effect by the intensity as appropriate based its intent. */
|
/** Scale the vibration effect by the intensity as appropriate based its intent. */
|
||||||
private void applyVibrationIntensityScalingLocked(Vibration vib) {
|
private void applyVibrationIntensityScalingLocked(Vibration vib) {
|
||||||
vib.updateEffect(mVibrationScaler.scale(vib.getEffect(), vib.attrs.getUsage()));
|
vib.updateEffect(mVibrationScaler.scale(vib.getEffect(), vib.attrs.getUsage()));
|
||||||
@@ -665,13 +671,14 @@ public class VibratorService extends IVibratorService.Stub {
|
|||||||
|
|
||||||
@GuardedBy("mLock")
|
@GuardedBy("mLock")
|
||||||
private void reportFinishVibrationLocked(Vibration.Status status) {
|
private void reportFinishVibrationLocked(Vibration.Status status) {
|
||||||
Trace.asyncTraceEnd(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
|
|
||||||
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "reportFinishVibrationLocked");
|
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "reportFinishVibrationLocked");
|
||||||
try {
|
try {
|
||||||
if (mCurrentVibration != null) {
|
if (mCurrentVibration != null) {
|
||||||
endVibrationLocked(mCurrentVibration, status);
|
endVibrationLocked(mCurrentVibration, status);
|
||||||
mAppOps.finishOp(AppOpsManager.OP_VIBRATE, mCurrentVibration.uid,
|
mAppOps.finishOp(AppOpsManager.OP_VIBRATE, mCurrentVibration.uid,
|
||||||
mCurrentVibration.opPkg);
|
mCurrentVibration.opPkg);
|
||||||
|
|
||||||
|
Trace.asyncTraceEnd(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
|
||||||
mCurrentVibration = null;
|
mCurrentVibration = null;
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
@@ -697,21 +704,6 @@ public class VibratorService extends IVibratorService.Stub {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void doVibratorOff() {
|
|
||||||
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doVibratorOff");
|
|
||||||
try {
|
|
||||||
if (DEBUG) {
|
|
||||||
Slog.d(TAG, "Turning vibrator off.");
|
|
||||||
}
|
|
||||||
boolean inputDevicesAvailable = mInputDeviceDelegate.cancelVibrateIfAvailable();
|
|
||||||
if (!inputDevicesAvailable) {
|
|
||||||
mVibratorController.off();
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isSystemHapticFeedback(Vibration vib) {
|
private boolean isSystemHapticFeedback(Vibration vib) {
|
||||||
if (vib.attrs.getUsage() != VibrationAttributes.USAGE_TOUCH) {
|
if (vib.attrs.getUsage() != VibrationAttributes.USAGE_TOUCH) {
|
||||||
return false;
|
return false;
|
||||||
@@ -844,6 +836,7 @@ public class VibratorService extends IVibratorService.Stub {
|
|||||||
// haptic feedback as part of the transition. So we don't cancel
|
// haptic feedback as part of the transition. So we don't cancel
|
||||||
// system vibrations.
|
// system vibrations.
|
||||||
if (mCurrentVibration != null && !isSystemHapticFeedback(mCurrentVibration)) {
|
if (mCurrentVibration != null && !isSystemHapticFeedback(mCurrentVibration)) {
|
||||||
|
mNextVibrationThread = null;
|
||||||
doCancelVibrateLocked(Vibration.Status.CANCELLED);
|
doCancelVibrateLocked(Vibration.Status.CANCELLED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -910,6 +903,8 @@ public class VibratorService extends IVibratorService.Stub {
|
|||||||
return IExternalVibratorService.SCALE_MUTE;
|
return IExternalVibratorService.SCALE_MUTE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VibrationThread cancelingVibration = null;
|
||||||
|
int scale;
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
if (mCurrentExternalVibration != null
|
if (mCurrentExternalVibration != null
|
||||||
&& mCurrentExternalVibration.externalVibration.equals(vib)) {
|
&& mCurrentExternalVibration.externalVibration.equals(vib)) {
|
||||||
@@ -920,11 +915,9 @@ public class VibratorService extends IVibratorService.Stub {
|
|||||||
if (mCurrentExternalVibration == null) {
|
if (mCurrentExternalVibration == null) {
|
||||||
// If we're not under external control right now, then cancel any normal
|
// If we're not under external control right now, then cancel any normal
|
||||||
// vibration that may be playing and ready the vibrator for external control.
|
// vibration that may be playing and ready the vibrator for external control.
|
||||||
if (DEBUG) {
|
mNextVibrationThread = null;
|
||||||
Slog.d(TAG, "Vibrator going under external control.");
|
|
||||||
}
|
|
||||||
doCancelVibrateLocked(Vibration.Status.CANCELLED);
|
doCancelVibrateLocked(Vibration.Status.CANCELLED);
|
||||||
mVibratorController.setExternalControl(true);
|
cancelingVibration = mThread;
|
||||||
} else {
|
} else {
|
||||||
endVibrationLocked(mCurrentExternalVibration, Vibration.Status.CANCELLED);
|
endVibrationLocked(mCurrentExternalVibration, Vibration.Status.CANCELLED);
|
||||||
}
|
}
|
||||||
@@ -941,11 +934,24 @@ public class VibratorService extends IVibratorService.Stub {
|
|||||||
vib.linkToDeath(mCurrentExternalDeathRecipient);
|
vib.linkToDeath(mCurrentExternalDeathRecipient);
|
||||||
mCurrentExternalVibration.scale = mVibrationScaler.getExternalVibrationScale(
|
mCurrentExternalVibration.scale = mVibrationScaler.getExternalVibrationScale(
|
||||||
vib.getVibrationAttributes().getUsage());
|
vib.getVibrationAttributes().getUsage());
|
||||||
|
scale = mCurrentExternalVibration.scale;
|
||||||
|
}
|
||||||
|
if (cancelingVibration != null) {
|
||||||
|
try {
|
||||||
|
cancelingVibration.join();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Slog.w("Interrupted while waiting current vibration to be cancelled before "
|
||||||
|
+ "starting external vibration", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (DEBUG) {
|
||||||
|
Slog.d(TAG, "Vibrator going under external control.");
|
||||||
|
}
|
||||||
|
mVibratorController.setExternalControl(true);
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Slog.e(TAG, "Playing external vibration: " + vib);
|
Slog.e(TAG, "Playing external vibration: " + vib);
|
||||||
}
|
}
|
||||||
return mCurrentExternalVibration.scale;
|
return scale;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi
|
|||||||
void onVibrationEnded(long vibrationId, Vibration.Status status);
|
void onVibrationEnded(long vibrationId, Vibration.Status status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final Object mLock = new Object();
|
||||||
private final WorkSource mWorkSource = new WorkSource();
|
private final WorkSource mWorkSource = new WorkSource();
|
||||||
private final PowerManager.WakeLock mWakeLock;
|
private final PowerManager.WakeLock mWakeLock;
|
||||||
private final IBatteryStats mBatteryStatsService;
|
private final IBatteryStats mBatteryStatsService;
|
||||||
@@ -81,10 +82,10 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi
|
|||||||
private final VibrationCallbacks mCallbacks;
|
private final VibrationCallbacks mCallbacks;
|
||||||
private final SparseArray<VibratorController> mVibrators;
|
private final SparseArray<VibratorController> mVibrators;
|
||||||
|
|
||||||
@GuardedBy("this")
|
@GuardedBy("mLock")
|
||||||
@Nullable
|
@Nullable
|
||||||
private VibrateStep mCurrentVibrateStep;
|
private VibrateStep mCurrentVibrateStep;
|
||||||
@GuardedBy("this")
|
@GuardedBy("mLock")
|
||||||
private boolean mForceStop;
|
private boolean mForceStop;
|
||||||
|
|
||||||
// TODO(b/159207608): Remove this constructor once VibratorService is removed
|
// TODO(b/159207608): Remove this constructor once VibratorService is removed
|
||||||
@@ -113,6 +114,10 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Vibration getVibration() {
|
||||||
|
return mVibration;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void binderDied() {
|
public void binderDied() {
|
||||||
cancel();
|
cancel();
|
||||||
@@ -136,15 +141,15 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi
|
|||||||
|
|
||||||
/** Cancel current vibration and shuts down the thread gracefully. */
|
/** Cancel current vibration and shuts down the thread gracefully. */
|
||||||
public void cancel() {
|
public void cancel() {
|
||||||
synchronized (this) {
|
synchronized (mLock) {
|
||||||
mForceStop = true;
|
mForceStop = true;
|
||||||
notify();
|
mLock.notify();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Notify current vibration that a step has completed on given vibrator. */
|
/** Notify current vibration that a step has completed on given vibrator. */
|
||||||
public void vibratorComplete(int vibratorId) {
|
public void vibratorComplete(int vibratorId) {
|
||||||
synchronized (this) {
|
synchronized (mLock) {
|
||||||
if (mCurrentVibrateStep != null) {
|
if (mCurrentVibrateStep != null) {
|
||||||
mCurrentVibrateStep.vibratorComplete(vibratorId);
|
mCurrentVibrateStep.vibratorComplete(vibratorId);
|
||||||
}
|
}
|
||||||
@@ -168,7 +173,7 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi
|
|||||||
final int stepCount = steps.size();
|
final int stepCount = steps.size();
|
||||||
for (int i = 0; i < stepCount; i++) {
|
for (int i = 0; i < stepCount; i++) {
|
||||||
Step step = steps.get(i);
|
Step step = steps.get(i);
|
||||||
synchronized (this) {
|
synchronized (mLock) {
|
||||||
if (step instanceof VibrateStep) {
|
if (step instanceof VibrateStep) {
|
||||||
mCurrentVibrateStep = (VibrateStep) step;
|
mCurrentVibrateStep = (VibrateStep) step;
|
||||||
} else {
|
} else {
|
||||||
@@ -295,21 +300,48 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi
|
|||||||
* Sleeps until given {@code wakeUpTime}.
|
* Sleeps until given {@code wakeUpTime}.
|
||||||
*
|
*
|
||||||
* <p>This stops immediately when {@link #cancel()} is called.
|
* <p>This stops immediately when {@link #cancel()} is called.
|
||||||
|
*
|
||||||
|
* @return true if waited until wake-up time, false if it was cancelled.
|
||||||
*/
|
*/
|
||||||
private void waitUntil(long wakeUpTime) {
|
private boolean waitUntil(long wakeUpTime) {
|
||||||
synchronized (this) {
|
synchronized (mLock) {
|
||||||
long durationRemaining = wakeUpTime - SystemClock.uptimeMillis();
|
long durationRemaining = wakeUpTime - SystemClock.uptimeMillis();
|
||||||
while (durationRemaining > 0) {
|
while (durationRemaining > 0) {
|
||||||
try {
|
try {
|
||||||
VibrationThread.this.wait(durationRemaining);
|
mLock.wait(durationRemaining);
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
}
|
}
|
||||||
if (mForceStop) {
|
if (mForceStop) {
|
||||||
break;
|
return false;
|
||||||
}
|
}
|
||||||
durationRemaining = wakeUpTime - SystemClock.uptimeMillis();
|
durationRemaining = wakeUpTime - SystemClock.uptimeMillis();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sleeps until given {@link VibrateStep#isVibrationComplete()}, or until {@code wakeUpTime}.
|
||||||
|
*
|
||||||
|
* <p>This stops immediately when {@link #cancel()} is called.
|
||||||
|
*
|
||||||
|
* @return true if finished on vibration complete, false if it was cancelled or timed out.
|
||||||
|
*/
|
||||||
|
private boolean waitForVibrationComplete(VibrateStep step, long wakeUpTime) {
|
||||||
|
synchronized (mLock) {
|
||||||
|
long durationRemaining = wakeUpTime - SystemClock.uptimeMillis();
|
||||||
|
while (!step.isVibrationComplete() && durationRemaining > 0) {
|
||||||
|
try {
|
||||||
|
mLock.wait(durationRemaining);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
}
|
||||||
|
if (mForceStop) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
durationRemaining = wakeUpTime - SystemClock.uptimeMillis();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return step.isVibrationComplete();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void noteVibratorOn(long duration) {
|
private void noteVibratorOn(long duration) {
|
||||||
@@ -341,6 +373,9 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi
|
|||||||
private interface VibrateStep extends Step {
|
private interface VibrateStep extends Step {
|
||||||
/** Callback to notify a vibrator has finished playing a effect. */
|
/** Callback to notify a vibrator has finished playing a effect. */
|
||||||
void vibratorComplete(int vibratorId);
|
void vibratorComplete(int vibratorId);
|
||||||
|
|
||||||
|
/** Returns true if the vibration played by this step is complete. */
|
||||||
|
boolean isVibrationComplete();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Represent a vibration on a single vibrator. */
|
/** Represent a vibration on a single vibrator. */
|
||||||
@@ -348,11 +383,20 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi
|
|||||||
private final VibratorController mVibrator;
|
private final VibratorController mVibrator;
|
||||||
private final VibrationEffect mEffect;
|
private final VibrationEffect mEffect;
|
||||||
|
|
||||||
|
@GuardedBy("mLock")
|
||||||
|
private boolean mVibrationComplete;
|
||||||
|
|
||||||
SingleVibrateStep(VibratorController vibrator, VibrationEffect effect) {
|
SingleVibrateStep(VibratorController vibrator, VibrationEffect effect) {
|
||||||
mVibrator = vibrator;
|
mVibrator = vibrator;
|
||||||
mEffect = effect;
|
mEffect = effect;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GuardedBy("mLock")
|
||||||
|
@Override
|
||||||
|
public boolean isVibrationComplete() {
|
||||||
|
return mVibrationComplete;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void vibratorComplete(int vibratorId) {
|
public void vibratorComplete(int vibratorId) {
|
||||||
if (mVibrator.getVibratorInfo().getId() != vibratorId) {
|
if (mVibrator.getVibratorInfo().getId() != vibratorId) {
|
||||||
@@ -364,8 +408,9 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mVibrator.off();
|
mVibrator.off();
|
||||||
synchronized (VibrationThread.this) {
|
synchronized (mLock) {
|
||||||
VibrationThread.this.notify();
|
mVibrationComplete = true;
|
||||||
|
mLock.notify();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -384,13 +429,14 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi
|
|||||||
noteVibratorOn(duration);
|
noteVibratorOn(duration);
|
||||||
// Vibration is playing with no need to control amplitudes, just wait for native
|
// Vibration is playing with no need to control amplitudes, just wait for native
|
||||||
// callback or timeout.
|
// callback or timeout.
|
||||||
waitUntil(startTime + duration + CALLBACKS_EXTRA_TIMEOUT);
|
if (waitForVibrationComplete(this,
|
||||||
if (mForceStop) {
|
startTime + duration + CALLBACKS_EXTRA_TIMEOUT)) {
|
||||||
mVibrator.off();
|
|
||||||
return Vibration.Status.CANCELLED;
|
|
||||||
}
|
|
||||||
return Vibration.Status.FINISHED;
|
return Vibration.Status.FINISHED;
|
||||||
}
|
}
|
||||||
|
// Timed out or vibration cancelled. Stop vibrator anyway.
|
||||||
|
mVibrator.off();
|
||||||
|
return mForceStop ? Vibration.Status.CANCELLED : Vibration.Status.FINISHED;
|
||||||
|
}
|
||||||
|
|
||||||
startTime = SystemClock.uptimeMillis();
|
startTime = SystemClock.uptimeMillis();
|
||||||
AmplitudeStep amplitudeStep = vibrateWithAmplitude(mEffect, startTime);
|
AmplitudeStep amplitudeStep = vibrateWithAmplitude(mEffect, startTime);
|
||||||
@@ -407,8 +453,7 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi
|
|||||||
noteVibratorOn(duration);
|
noteVibratorOn(duration);
|
||||||
}
|
}
|
||||||
while (amplitudeStep != null) {
|
while (amplitudeStep != null) {
|
||||||
waitUntil(amplitudeStep.startTime);
|
if (!waitUntil(amplitudeStep.startTime)) {
|
||||||
if (mForceStop) {
|
|
||||||
mVibrator.off();
|
mVibrator.off();
|
||||||
return Vibration.Status.CANCELLED;
|
return Vibration.Status.CANCELLED;
|
||||||
}
|
}
|
||||||
@@ -482,7 +527,7 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi
|
|||||||
private final int mRequiredCapabilities;
|
private final int mRequiredCapabilities;
|
||||||
private final int[] mVibratorIds;
|
private final int[] mVibratorIds;
|
||||||
|
|
||||||
@GuardedBy("VibrationThread.this")
|
@GuardedBy("mLock")
|
||||||
private int mActiveVibratorCounter;
|
private int mActiveVibratorCounter;
|
||||||
|
|
||||||
SyncedVibrateStep(SparseArray<VibrationEffect> effects) {
|
SyncedVibrateStep(SparseArray<VibrationEffect> effects) {
|
||||||
@@ -496,6 +541,12 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GuardedBy("mLock")
|
||||||
|
@Override
|
||||||
|
public boolean isVibrationComplete() {
|
||||||
|
return mActiveVibratorCounter <= 0;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void vibratorComplete(int vibratorId) {
|
public void vibratorComplete(int vibratorId) {
|
||||||
VibrationEffect effect = mEffects.get(vibratorId);
|
VibrationEffect effect = mEffects.get(vibratorId);
|
||||||
@@ -508,10 +559,9 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mVibrators.get(vibratorId).off();
|
mVibrators.get(vibratorId).off();
|
||||||
synchronized (VibrationThread.this) {
|
synchronized (mLock) {
|
||||||
if (--mActiveVibratorCounter <= 0) {
|
--mActiveVibratorCounter;
|
||||||
VibrationThread.this.notify();
|
mLock.notify();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -532,8 +582,7 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi
|
|||||||
|
|
||||||
while (!nextSteps.isEmpty()) {
|
while (!nextSteps.isEmpty()) {
|
||||||
AmplitudeStep step = nextSteps.poll();
|
AmplitudeStep step = nextSteps.poll();
|
||||||
waitUntil(step.startTime);
|
if (!waitUntil(step.startTime)) {
|
||||||
if (mForceStop) {
|
|
||||||
stopAllVibrators();
|
stopAllVibrators();
|
||||||
return Vibration.Status.CANCELLED;
|
return Vibration.Status.CANCELLED;
|
||||||
}
|
}
|
||||||
@@ -541,7 +590,7 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi
|
|||||||
AmplitudeStep nextStep = step.nextStep();
|
AmplitudeStep nextStep = step.nextStep();
|
||||||
if (nextStep == null) {
|
if (nextStep == null) {
|
||||||
// This vibrator has finished playing the effect for this step.
|
// This vibrator has finished playing the effect for this step.
|
||||||
synchronized (VibrationThread.this) {
|
synchronized (mLock) {
|
||||||
mActiveVibratorCounter--;
|
mActiveVibratorCounter--;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -549,19 +598,18 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// All OneShot and Waveform effects have finished. Just wait for the other effects
|
synchronized (mLock) {
|
||||||
// to end via native callbacks before finishing this synced step.
|
// All OneShot and Waveform effects have finished. Just wait for the other
|
||||||
synchronized (VibrationThread.this) {
|
// effects to end via native callbacks before finishing this synced step.
|
||||||
if (mActiveVibratorCounter > 0) {
|
final long wakeUpTime = startTime + timeout + CALLBACKS_EXTRA_TIMEOUT;
|
||||||
waitUntil(startTime + timeout + CALLBACKS_EXTRA_TIMEOUT);
|
if (mActiveVibratorCounter <= 0 || waitForVibrationComplete(this, wakeUpTime)) {
|
||||||
}
|
return Vibration.Status.FINISHED;
|
||||||
}
|
|
||||||
if (mForceStop) {
|
|
||||||
stopAllVibrators();
|
|
||||||
return Vibration.Status.CANCELLED;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Vibration.Status.FINISHED;
|
// Timed out or vibration cancelled. Stop all vibrators anyway.
|
||||||
|
stopAllVibrators();
|
||||||
|
return mForceStop ? Vibration.Status.CANCELLED : Vibration.Status.FINISHED;
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
if (timeout > 0) {
|
if (timeout > 0) {
|
||||||
noteVibratorOff();
|
noteVibratorOff();
|
||||||
@@ -774,8 +822,10 @@ public final class VibrationThread extends Thread implements IBinder.DeathRecipi
|
|||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Slog.d(TAG, "DelayStep of " + mDelay + "ms starting...");
|
Slog.d(TAG, "DelayStep of " + mDelay + "ms starting...");
|
||||||
}
|
}
|
||||||
waitUntil(SystemClock.uptimeMillis() + mDelay);
|
if (waitUntil(SystemClock.uptimeMillis() + mDelay)) {
|
||||||
return mForceStop ? Vibration.Status.CANCELLED : Vibration.Status.FINISHED;
|
return Vibration.Status.FINISHED;
|
||||||
|
}
|
||||||
|
return Vibration.Status.CANCELLED;
|
||||||
} finally {
|
} finally {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Slog.d(TAG, "DelayStep done.");
|
Slog.d(TAG, "DelayStep done.");
|
||||||
|
|||||||
Reference in New Issue
Block a user