Merge "Add support to slowly bring the vibrator down in VibrationThread" into sc-dev
This commit is contained in:
@@ -3370,8 +3370,7 @@
|
|||||||
<integer name="config_vibrationWaveformRampStepDuration">5</integer>
|
<integer name="config_vibrationWaveformRampStepDuration">5</integer>
|
||||||
|
|
||||||
<!-- The duration (in milliseconds) that should be applied to waveform vibrations that ends in
|
<!-- The duration (in milliseconds) that should be applied to waveform vibrations that ends in
|
||||||
non-zero amplitudes, . The waveform will
|
non-zero amplitudes, to bring the vibrator amplitude down to zero using this timing. -->
|
||||||
be played as a PWLE instead of on/off calls if this value is set. -->
|
|
||||||
<integer name="config_vibrationWaveformRampDownDuration">0</integer>
|
<integer name="config_vibrationWaveformRampDownDuration">0</integer>
|
||||||
|
|
||||||
<!-- Number of retries Cell Data should attempt for a given error code before
|
<!-- Number of retries Cell Data should attempt for a given error code before
|
||||||
|
|||||||
@@ -99,16 +99,24 @@ final class VibrationSettings {
|
|||||||
private boolean mLowPowerMode;
|
private boolean mLowPowerMode;
|
||||||
|
|
||||||
VibrationSettings(Context context, Handler handler) {
|
VibrationSettings(Context context, Handler handler) {
|
||||||
|
this(context, handler,
|
||||||
|
context.getResources().getInteger(
|
||||||
|
com.android.internal.R.integer.config_vibrationWaveformRampDownDuration),
|
||||||
|
context.getResources().getInteger(
|
||||||
|
com.android.internal.R.integer.config_vibrationWaveformRampStepDuration));
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
VibrationSettings(Context context, Handler handler, int rampDownDuration,
|
||||||
|
int rampStepDuration) {
|
||||||
mContext = context;
|
mContext = context;
|
||||||
mSettingObserver = new SettingsObserver(handler);
|
mSettingObserver = new SettingsObserver(handler);
|
||||||
mUidObserver = new UidObserver();
|
mUidObserver = new UidObserver();
|
||||||
mUserReceiver = new UserObserver();
|
mUserReceiver = new UserObserver();
|
||||||
|
|
||||||
// TODO(b/191150049): move these to vibrator static config file
|
// TODO(b/191150049): move these to vibrator static config file
|
||||||
mRampStepDuration = context.getResources().getInteger(
|
mRampDownDuration = rampDownDuration;
|
||||||
com.android.internal.R.integer.config_vibrationWaveformRampStepDuration);
|
mRampStepDuration = rampStepDuration;
|
||||||
mRampDownDuration = context.getResources().getInteger(
|
|
||||||
com.android.internal.R.integer.config_vibrationWaveformRampDownDuration);
|
|
||||||
|
|
||||||
VibrationEffect clickEffect = createEffectFromResource(
|
VibrationEffect clickEffect = createEffectFromResource(
|
||||||
com.android.internal.R.array.config_virtualKeyVibePattern);
|
com.android.internal.R.array.config_virtualKeyVibePattern);
|
||||||
|
|||||||
@@ -61,6 +61,9 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
|||||||
*/
|
*/
|
||||||
private static final long CALLBACKS_EXTRA_TIMEOUT = 100;
|
private static final long CALLBACKS_EXTRA_TIMEOUT = 100;
|
||||||
|
|
||||||
|
/** Threshold to prevent the ramp off steps from trying to set extremely low amplitudes. */
|
||||||
|
private static final float RAMP_OFF_AMPLITUDE_MIN = 1e-3f;
|
||||||
|
|
||||||
/** Fixed large duration used to note repeating vibrations to {@link IBatteryStats}. */
|
/** Fixed large duration used to note repeating vibrations to {@link IBatteryStats}. */
|
||||||
private static final long BATTERY_STATS_REPEATING_VIBRATION_DURATION = 5_000;
|
private static final long BATTERY_STATS_REPEATING_VIBRATION_DURATION = 5_000;
|
||||||
|
|
||||||
@@ -87,26 +90,33 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
|||||||
/** Callback triggered to cancel a prepared synced vibration. */
|
/** Callback triggered to cancel a prepared synced vibration. */
|
||||||
void cancelSyncedVibration();
|
void cancelSyncedVibration();
|
||||||
|
|
||||||
/** Callback triggered when vibration thread is complete. */
|
/** Callback triggered when the vibration is complete. */
|
||||||
void onVibrationEnded(long vibrationId, Vibration.Status status);
|
void onVibrationCompleted(long vibrationId, Vibration.Status status);
|
||||||
|
|
||||||
|
/** Callback triggered when the vibrators are released after the thread is complete. */
|
||||||
|
void onVibratorsReleased();
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Object mLock = new Object();
|
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;
|
||||||
|
private final VibrationSettings mVibrationSettings;
|
||||||
private final DeviceVibrationEffectAdapter mDeviceEffectAdapter;
|
private final DeviceVibrationEffectAdapter mDeviceEffectAdapter;
|
||||||
private final Vibration mVibration;
|
private final Vibration mVibration;
|
||||||
private final VibrationCallbacks mCallbacks;
|
private final VibrationCallbacks mCallbacks;
|
||||||
private final SparseArray<VibratorController> mVibrators = new SparseArray<>();
|
private final SparseArray<VibratorController> mVibrators = new SparseArray<>();
|
||||||
private final StepQueue mStepQueue = new StepQueue();
|
private final StepQueue mStepQueue = new StepQueue();
|
||||||
|
|
||||||
|
private volatile boolean mStop;
|
||||||
private volatile boolean mForceStop;
|
private volatile boolean mForceStop;
|
||||||
|
|
||||||
VibrationThread(Vibration vib, DeviceVibrationEffectAdapter effectAdapter,
|
VibrationThread(Vibration vib, VibrationSettings vibrationSettings,
|
||||||
|
DeviceVibrationEffectAdapter effectAdapter,
|
||||||
SparseArray<VibratorController> availableVibrators, PowerManager.WakeLock wakeLock,
|
SparseArray<VibratorController> availableVibrators, PowerManager.WakeLock wakeLock,
|
||||||
IBatteryStats batteryStatsService, VibrationCallbacks callbacks) {
|
IBatteryStats batteryStatsService, VibrationCallbacks callbacks) {
|
||||||
mVibration = vib;
|
mVibration = vib;
|
||||||
|
mVibrationSettings = vibrationSettings;
|
||||||
mDeviceEffectAdapter = effectAdapter;
|
mDeviceEffectAdapter = effectAdapter;
|
||||||
mCallbacks = callbacks;
|
mCallbacks = callbacks;
|
||||||
mWakeLock = wakeLock;
|
mWakeLock = wakeLock;
|
||||||
@@ -145,8 +155,8 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
|||||||
mWakeLock.acquire();
|
mWakeLock.acquire();
|
||||||
try {
|
try {
|
||||||
mVibration.token.linkToDeath(this, 0);
|
mVibration.token.linkToDeath(this, 0);
|
||||||
Vibration.Status status = playVibration();
|
playVibration();
|
||||||
mCallbacks.onVibrationEnded(mVibration.id, status);
|
mCallbacks.onVibratorsReleased();
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
Slog.e(TAG, "Error linking vibration to token death", e);
|
Slog.e(TAG, "Error linking vibration to token death", e);
|
||||||
} finally {
|
} finally {
|
||||||
@@ -155,9 +165,13 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Cancel current vibration and shuts down the thread gracefully. */
|
/** Cancel current vibration and ramp down the vibrators gracefully. */
|
||||||
public void cancel() {
|
public void cancel() {
|
||||||
mForceStop = true;
|
if (mStop) {
|
||||||
|
// Already cancelled, running clean-up steps.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mStop = true;
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Slog.d(TAG, "Vibration cancelled");
|
Slog.d(TAG, "Vibration cancelled");
|
||||||
@@ -166,6 +180,21 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Cancel current vibration and shuts off the vibrators immediately. */
|
||||||
|
public void cancelImmediately() {
|
||||||
|
if (mForceStop) {
|
||||||
|
// Already forced the thread to stop, wait for it to finish.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mStop = mForceStop = true;
|
||||||
|
synchronized (mLock) {
|
||||||
|
if (DEBUG) {
|
||||||
|
Slog.d(TAG, "Vibration cancelled immediately");
|
||||||
|
}
|
||||||
|
mLock.notify();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Notify current vibration that a synced step has completed. */
|
/** Notify current vibration that a synced step has completed. */
|
||||||
public void syncedVibrationComplete() {
|
public void syncedVibrationComplete() {
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
@@ -190,17 +219,18 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Vibration.Status playVibration() {
|
private void playVibration() {
|
||||||
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "playVibration");
|
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "playVibration");
|
||||||
try {
|
try {
|
||||||
CombinedVibration.Sequential effect = toSequential(mVibration.getEffect());
|
CombinedVibration.Sequential sequentialEffect = toSequential(mVibration.getEffect());
|
||||||
mStepQueue.offer(new StartVibrateStep(effect));
|
final int sequentialEffectSize = sequentialEffect.getEffects().size();
|
||||||
|
mStepQueue.offer(new StartVibrateStep(sequentialEffect));
|
||||||
|
|
||||||
int stepsPlayed = 0;
|
Vibration.Status status = null;
|
||||||
while (!mStepQueue.isEmpty()) {
|
while (!mStepQueue.isEmpty()) {
|
||||||
long waitTime = mStepQueue.calculateWaitTime();
|
long waitTime = mStepQueue.calculateWaitTime();
|
||||||
if (waitTime <= 0) {
|
if (waitTime <= 0) {
|
||||||
stepsPlayed += mStepQueue.consumeNext();
|
mStepQueue.consumeNext();
|
||||||
} else {
|
} else {
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
try {
|
try {
|
||||||
@@ -209,17 +239,33 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (mForceStop) {
|
Vibration.Status currentStatus = mStop ? Vibration.Status.CANCELLED
|
||||||
|
: mStepQueue.calculateVibrationStatus(sequentialEffectSize);
|
||||||
|
if (status == null && currentStatus != Vibration.Status.RUNNING) {
|
||||||
|
// First time vibration stopped running, start clean-up tasks and notify
|
||||||
|
// callback immediately.
|
||||||
|
status = currentStatus;
|
||||||
|
mCallbacks.onVibrationCompleted(mVibration.id, status);
|
||||||
|
if (status == Vibration.Status.CANCELLED) {
|
||||||
mStepQueue.cancel();
|
mStepQueue.cancel();
|
||||||
return Vibration.Status.CANCELLED;
|
}
|
||||||
|
}
|
||||||
|
if (mForceStop) {
|
||||||
|
// Cancel every step and stop playing them right away, even clean-up steps.
|
||||||
|
mStepQueue.cancelImmediately();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Some effects might be ignored because the specified vibrator don't exist or doesn't
|
if (status == null) {
|
||||||
// support the effect. We only report ignored here if nothing was played besides the
|
status = mStepQueue.calculateVibrationStatus(sequentialEffectSize);
|
||||||
// StartVibrateStep (which means every attempt to turn on the vibrator was ignored).
|
if (status == Vibration.Status.RUNNING) {
|
||||||
return stepsPlayed > effect.getEffects().size()
|
Slog.w(TAG, "Something went wrong, step queue completed but vibration status"
|
||||||
? Vibration.Status.FINISHED : Vibration.Status.IGNORED_UNSUPPORTED;
|
+ " is still RUNNING for vibration " + mVibration.id);
|
||||||
|
status = Vibration.Status.FINISHED;
|
||||||
|
}
|
||||||
|
mCallbacks.onVibrationCompleted(mVibration.id, status);
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
|
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
|
||||||
}
|
}
|
||||||
@@ -260,12 +306,9 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
|||||||
segmentIndex = effect.getRepeatIndex();
|
segmentIndex = effect.getRepeatIndex();
|
||||||
}
|
}
|
||||||
if (segmentIndex < 0) {
|
if (segmentIndex < 0) {
|
||||||
if (vibratorOffTimeout > SystemClock.uptimeMillis()) {
|
// No more segments to play, last step is to complete the vibration on this vibrator.
|
||||||
// No more segments to play, last step is to wait for the vibrator to complete
|
return new CompleteStep(startTime, /* cancelled= */ false, controller,
|
||||||
return new OffStep(vibratorOffTimeout, controller);
|
vibratorOffTimeout);
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
VibrationEffectSegment segment = effect.getSegments().get(segmentIndex);
|
VibrationEffectSegment segment = effect.getSegments().get(segmentIndex);
|
||||||
@@ -299,8 +342,18 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
|||||||
@GuardedBy("mLock")
|
@GuardedBy("mLock")
|
||||||
private final Queue<Step> mPendingOnVibratorCompleteSteps = new LinkedList<>();
|
private final Queue<Step> mPendingOnVibratorCompleteSteps = new LinkedList<>();
|
||||||
|
|
||||||
|
@GuardedBy("mLock")
|
||||||
|
private int mPendingVibrateSteps;
|
||||||
|
@GuardedBy("mLock")
|
||||||
|
private int mConsumedStartVibrateSteps;
|
||||||
|
@GuardedBy("mLock")
|
||||||
|
private int mSuccessfulVibratorOnSteps;
|
||||||
|
|
||||||
public void offer(@NonNull Step step) {
|
public void offer(@NonNull Step step) {
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
|
if (!step.isCleanUp()) {
|
||||||
|
mPendingVibrateSteps++;
|
||||||
|
}
|
||||||
mNextSteps.offer(step);
|
mNextSteps.offer(step);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -311,6 +364,24 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the {@link Vibration.Status} based on the current queue state and the expected
|
||||||
|
* number of {@link StartVibrateStep} to be played.
|
||||||
|
*/
|
||||||
|
public Vibration.Status calculateVibrationStatus(int expectedStartVibrateSteps) {
|
||||||
|
synchronized (mLock) {
|
||||||
|
if (mPendingVibrateSteps > 0
|
||||||
|
|| mConsumedStartVibrateSteps < expectedStartVibrateSteps) {
|
||||||
|
return Vibration.Status.RUNNING;
|
||||||
|
}
|
||||||
|
if (mSuccessfulVibratorOnSteps > 0) {
|
||||||
|
return Vibration.Status.FINISHED;
|
||||||
|
}
|
||||||
|
// If no step was able to turn the vibrator ON successfully.
|
||||||
|
return Vibration.Status.IGNORED_UNSUPPORTED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Returns the time in millis to wait before calling {@link #consumeNext()}. */
|
/** Returns the time in millis to wait before calling {@link #consumeNext()}. */
|
||||||
public long calculateWaitTime() {
|
public long calculateWaitTime() {
|
||||||
Step nextStep;
|
Step nextStep;
|
||||||
@@ -330,18 +401,28 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
|||||||
*
|
*
|
||||||
* @return the number of steps played
|
* @return the number of steps played
|
||||||
*/
|
*/
|
||||||
public int consumeNext() {
|
public void consumeNext() {
|
||||||
Step nextStep = pollNext();
|
Step nextStep = pollNext();
|
||||||
if (nextStep != null) {
|
if (nextStep != null) {
|
||||||
// This might turn on the vibrator and have a HAL latency. Execute this outside any
|
// This might turn on the vibrator and have a HAL latency. Execute this outside any
|
||||||
// lock to avoid blocking other interactions with the thread.
|
// lock to avoid blocking other interactions with the thread.
|
||||||
List<Step> nextSteps = nextStep.play();
|
List<Step> nextSteps = nextStep.play();
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
|
if (nextStep.getVibratorOnDuration() > 0) {
|
||||||
|
mSuccessfulVibratorOnSteps++;
|
||||||
|
}
|
||||||
|
if (nextStep instanceof StartVibrateStep) {
|
||||||
|
mConsumedStartVibrateSteps++;
|
||||||
|
}
|
||||||
|
if (!nextStep.isCleanUp()) {
|
||||||
|
mPendingVibrateSteps--;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < nextSteps.size(); i++) {
|
||||||
|
mPendingVibrateSteps += nextSteps.get(i).isCleanUp() ? 0 : 1;
|
||||||
|
}
|
||||||
mNextSteps.addAll(nextSteps);
|
mNextSteps.addAll(nextSteps);
|
||||||
}
|
}
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -368,16 +449,38 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cancel the current queue, clearing all remaining steps.
|
* Cancel the current queue, replacing all remaining steps with respective clean-up steps.
|
||||||
*
|
*
|
||||||
* <p>This will remove and trigger {@link Step#cancel()} in all steps, in order.
|
* <p>This will remove all steps and replace them with respective
|
||||||
|
* {@link Step#cancel()}.
|
||||||
*/
|
*/
|
||||||
public void cancel() {
|
public void cancel() {
|
||||||
|
List<Step> cleanUpSteps = new ArrayList<>();
|
||||||
|
Step step;
|
||||||
|
while ((step = pollNext()) != null) {
|
||||||
|
cleanUpSteps.addAll(step.cancel());
|
||||||
|
}
|
||||||
|
synchronized (mLock) {
|
||||||
|
// All steps generated by Step.cancel() should be clean-up steps.
|
||||||
|
mPendingVibrateSteps = 0;
|
||||||
|
mNextSteps.addAll(cleanUpSteps);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancel the current queue immediately, clearing all remaining steps and skipping clean-up.
|
||||||
|
*
|
||||||
|
* <p>This will remove and trigger {@link Step#cancelImmediately()} in all steps, in order.
|
||||||
|
*/
|
||||||
|
public void cancelImmediately() {
|
||||||
Step step;
|
Step step;
|
||||||
while ((step = pollNext()) != null) {
|
while ((step = pollNext()) != null) {
|
||||||
// This might turn off the vibrator and have a HAL latency. Execute this outside
|
// This might turn off the vibrator and have a HAL latency. Execute this outside
|
||||||
// any lock to avoid blocking other interactions with the thread.
|
// any lock to avoid blocking other interactions with the thread.
|
||||||
step.cancel();
|
step.cancelImmediately();
|
||||||
|
}
|
||||||
|
synchronized (mLock) {
|
||||||
|
mPendingVibrateSteps = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -406,12 +509,37 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
|||||||
this.startTime = startTime;
|
this.startTime = startTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if this step is a clean up step and not part of a {@link VibrationEffect} or
|
||||||
|
* {@link CombinedVibration}.
|
||||||
|
*/
|
||||||
|
public boolean isCleanUp() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/** Play this step, returning a (possibly empty) list of next steps. */
|
/** Play this step, returning a (possibly empty) list of next steps. */
|
||||||
@NonNull
|
@NonNull
|
||||||
public abstract List<Step> play();
|
public abstract List<Step> play();
|
||||||
|
|
||||||
/** Cancel this pending step. */
|
/**
|
||||||
public void cancel() {
|
* Cancel this pending step and return a (possibly empty) list of clean-up steps that should
|
||||||
|
* be played to gracefully cancel this step.
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
public abstract List<Step> cancel();
|
||||||
|
|
||||||
|
/** Cancel this pending step immediately, skipping any clean-up. */
|
||||||
|
public abstract void cancelImmediately();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the duration the vibrator was turned on when this step was played.
|
||||||
|
*
|
||||||
|
* @return A positive duration that the vibrator was turned on for by this step;
|
||||||
|
* Zero if the segment is not supported, the step was not played yet or vibrator was never
|
||||||
|
* turned on by this step; A negative value if the vibrator call has failed.
|
||||||
|
*/
|
||||||
|
public long getVibratorOnDuration() {
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -452,6 +580,8 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
|||||||
public final CombinedVibration.Sequential sequentialEffect;
|
public final CombinedVibration.Sequential sequentialEffect;
|
||||||
public final int currentIndex;
|
public final int currentIndex;
|
||||||
|
|
||||||
|
private long mVibratorsOnMaxDuration;
|
||||||
|
|
||||||
StartVibrateStep(CombinedVibration.Sequential effect) {
|
StartVibrateStep(CombinedVibration.Sequential effect) {
|
||||||
this(SystemClock.uptimeMillis() + effect.getDelays().get(0), effect, /* index= */ 0);
|
this(SystemClock.uptimeMillis() + effect.getDelays().get(0), effect, /* index= */ 0);
|
||||||
}
|
}
|
||||||
@@ -462,11 +592,16 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
|||||||
currentIndex = index;
|
currentIndex = index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getVibratorOnDuration() {
|
||||||
|
return mVibratorsOnMaxDuration;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Step> play() {
|
public List<Step> play() {
|
||||||
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "StartVibrateStep");
|
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "StartVibrateStep");
|
||||||
List<Step> nextSteps = new ArrayList<>();
|
List<Step> nextSteps = new ArrayList<>();
|
||||||
long duration = -1;
|
mVibratorsOnMaxDuration = -1;
|
||||||
try {
|
try {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Slog.d(TAG, "StartVibrateStep for effect #" + currentIndex);
|
Slog.d(TAG, "StartVibrateStep for effect #" + currentIndex);
|
||||||
@@ -478,25 +613,33 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
|||||||
return nextSteps;
|
return nextSteps;
|
||||||
}
|
}
|
||||||
|
|
||||||
duration = startVibrating(effectMapping, nextSteps);
|
mVibratorsOnMaxDuration = startVibrating(effectMapping, nextSteps);
|
||||||
noteVibratorOn(duration);
|
noteVibratorOn(mVibratorsOnMaxDuration);
|
||||||
} finally {
|
} finally {
|
||||||
if (duration < 0) {
|
if (mVibratorsOnMaxDuration >= 0) {
|
||||||
// Something failed while playing this step so stop playing this sequence.
|
|
||||||
return EMPTY_STEP_LIST;
|
|
||||||
}
|
|
||||||
// It least one vibrator was started then add a finish step to wait for all
|
// It least one vibrator was started then add a finish step to wait for all
|
||||||
// active vibrators to finish their individual steps before going to the next.
|
// active vibrators to finish their individual steps before going to the next.
|
||||||
// Otherwise this step was ignored so just go to the next one.
|
// Otherwise this step was ignored so just go to the next one.
|
||||||
Step nextStep = duration > 0 ? new FinishVibrateStep(this) : nextStep();
|
Step nextStep =
|
||||||
|
mVibratorsOnMaxDuration > 0 ? new FinishVibrateStep(this) : nextStep();
|
||||||
if (nextStep != null) {
|
if (nextStep != null) {
|
||||||
nextSteps.add(nextStep);
|
nextSteps.add(nextStep);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
|
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
|
||||||
}
|
}
|
||||||
return nextSteps;
|
return nextSteps;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Step> cancel() {
|
||||||
|
return EMPTY_STEP_LIST;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cancelImmediately() {
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create the next {@link StartVibrateStep} to play this sequential effect, starting at the
|
* Create the next {@link StartVibrateStep} to play this sequential effect, starting at the
|
||||||
* time this method is called, or null if sequence is complete.
|
* time this method is called, or null if sequence is complete.
|
||||||
@@ -593,7 +736,7 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
|||||||
// Some vibrator failed without being prepared so other vibrators might be
|
// Some vibrator failed without being prepared so other vibrators might be
|
||||||
// active. Cancel and remove every pending step from output list.
|
// active. Cancel and remove every pending step from output list.
|
||||||
for (int i = nextSteps.size() - 1; i >= 0; i--) {
|
for (int i = nextSteps.size() - 1; i >= 0; i--) {
|
||||||
nextSteps.remove(i).cancel();
|
nextSteps.remove(i).cancelImmediately();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -626,6 +769,12 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
|||||||
this.startedStep = startedStep;
|
this.startedStep = startedStep;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCleanUp() {
|
||||||
|
// This step only notes that all the vibrators has been turned off.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Step> play() {
|
public List<Step> play() {
|
||||||
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "FinishVibrateStep");
|
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "FinishVibrateStep");
|
||||||
@@ -642,7 +791,13 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void cancel() {
|
public List<Step> cancel() {
|
||||||
|
cancelImmediately();
|
||||||
|
return EMPTY_STEP_LIST;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cancelImmediately() {
|
||||||
noteVibratorOff();
|
noteVibratorOff();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -658,6 +813,7 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
|||||||
public final long vibratorOffTimeout;
|
public final long vibratorOffTimeout;
|
||||||
|
|
||||||
long mVibratorOnResult;
|
long mVibratorOnResult;
|
||||||
|
boolean mVibratorCallbackReceived;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param startTime The time to schedule this step in the {@link StepQueue}.
|
* @param startTime The time to schedule this step in the {@link StepQueue}.
|
||||||
@@ -678,27 +834,28 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
|||||||
this.vibratorOffTimeout = vibratorOffTimeout;
|
this.vibratorOffTimeout = vibratorOffTimeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Return the duration the vibrator was turned on when this step was played.
|
|
||||||
*
|
|
||||||
* @return A positive duration that the vibrator was turned on for by this step;
|
|
||||||
* Zero if the segment is not supported, the step was not played yet or vibrator was never
|
|
||||||
* turned on by this step; A negative value if the vibrator call has failed.
|
|
||||||
*/
|
|
||||||
public long getVibratorOnDuration() {
|
public long getVibratorOnDuration() {
|
||||||
return mVibratorOnResult;
|
return mVibratorOnResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean shouldPlayWhenVibratorComplete(int vibratorId) {
|
public boolean shouldPlayWhenVibratorComplete(int vibratorId) {
|
||||||
|
boolean isSameVibrator = controller.getVibratorInfo().getId() == vibratorId;
|
||||||
|
mVibratorCallbackReceived |= isSameVibrator;
|
||||||
// Only anticipate this step if a timeout was set to wait for the vibration to complete,
|
// Only anticipate this step if a timeout was set to wait for the vibration to complete,
|
||||||
// otherwise we are waiting for the correct time to play the next step.
|
// otherwise we are waiting for the correct time to play the next step.
|
||||||
return (controller.getVibratorInfo().getId() == vibratorId)
|
return isSameVibrator && (vibratorOffTimeout > SystemClock.uptimeMillis());
|
||||||
&& (vibratorOffTimeout > SystemClock.uptimeMillis());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void cancel() {
|
public List<Step> cancel() {
|
||||||
|
return Arrays.asList(new CompleteStep(SystemClock.uptimeMillis(),
|
||||||
|
/* cancelled= */ true, controller, vibratorOffTimeout));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cancelImmediately() {
|
||||||
if (vibratorOffTimeout > SystemClock.uptimeMillis()) {
|
if (vibratorOffTimeout > SystemClock.uptimeMillis()) {
|
||||||
// Vibrator might be running from previous steps, so turn it off while canceling.
|
// Vibrator might be running from previous steps, so turn it off while canceling.
|
||||||
stopVibrating();
|
stopVibrating();
|
||||||
@@ -712,6 +869,14 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
|||||||
controller.off();
|
controller.off();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void changeAmplitude(float amplitude) {
|
||||||
|
if (DEBUG) {
|
||||||
|
Slog.d(TAG, "Amplitude changed on vibrator " + controller.getVibratorInfo().getId()
|
||||||
|
+ " to " + amplitude);
|
||||||
|
}
|
||||||
|
controller.setAmplitude(amplitude);
|
||||||
|
}
|
||||||
|
|
||||||
/** Return the {@link #nextVibrateStep} with same timings, only jumping the segments. */
|
/** Return the {@link #nextVibrateStep} with same timings, only jumping the segments. */
|
||||||
public List<Step> skipToNextSteps(int segmentsSkipped) {
|
public List<Step> skipToNextSteps(int segmentsSkipped) {
|
||||||
return nextSteps(startTime, vibratorOffTimeout, segmentsSkipped);
|
return nextSteps(startTime, vibratorOffTimeout, segmentsSkipped);
|
||||||
@@ -937,6 +1102,140 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a step to complete a {@link VibrationEffect}.
|
||||||
|
*
|
||||||
|
* <p>This runs right at the time the vibration is considered to end and will update the pending
|
||||||
|
* vibrators count. This can turn off the vibrator or slowly ramp it down to zero amplitude.
|
||||||
|
*/
|
||||||
|
private final class CompleteStep extends SingleVibratorStep {
|
||||||
|
private final boolean mCancelled;
|
||||||
|
|
||||||
|
CompleteStep(long startTime, boolean cancelled, VibratorController controller,
|
||||||
|
long vibratorOffTimeout) {
|
||||||
|
super(startTime, controller, /* effect= */ null, /* index= */ -1, vibratorOffTimeout);
|
||||||
|
mCancelled = cancelled;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCleanUp() {
|
||||||
|
// If the vibration was cancelled then this is just a clean up to ramp off the vibrator.
|
||||||
|
// Otherwise this step is part of the vibration.
|
||||||
|
return mCancelled;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Step> cancel() {
|
||||||
|
if (mCancelled) {
|
||||||
|
// Double cancelling will just turn off the vibrator right away.
|
||||||
|
return Arrays.asList(new OffStep(SystemClock.uptimeMillis(), controller));
|
||||||
|
}
|
||||||
|
return super.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Step> play() {
|
||||||
|
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "CompleteStep");
|
||||||
|
try {
|
||||||
|
if (DEBUG) {
|
||||||
|
Slog.d(TAG, "Running " + (mCancelled ? "cancel" : "complete") + " vibration"
|
||||||
|
+ " step on vibrator " + controller.getVibratorInfo().getId());
|
||||||
|
}
|
||||||
|
if (mVibratorCallbackReceived) {
|
||||||
|
// Vibration completion callback was received by this step, just turn if off
|
||||||
|
// and skip any clean-up.
|
||||||
|
stopVibrating();
|
||||||
|
return EMPTY_STEP_LIST;
|
||||||
|
}
|
||||||
|
|
||||||
|
float currentAmplitude = controller.getCurrentAmplitude();
|
||||||
|
long remainingOnDuration =
|
||||||
|
vibratorOffTimeout - CALLBACKS_EXTRA_TIMEOUT - SystemClock.uptimeMillis();
|
||||||
|
long rampDownDuration =
|
||||||
|
Math.min(remainingOnDuration, mVibrationSettings.getRampDownDuration());
|
||||||
|
long stepDownDuration = mVibrationSettings.getRampStepDuration();
|
||||||
|
if (currentAmplitude < RAMP_OFF_AMPLITUDE_MIN
|
||||||
|
|| rampDownDuration <= stepDownDuration) {
|
||||||
|
// No need to ramp down the amplitude, just wait to turn it off.
|
||||||
|
if (mCancelled) {
|
||||||
|
// Vibration is completing because it was cancelled, turn off right away.
|
||||||
|
stopVibrating();
|
||||||
|
return EMPTY_STEP_LIST;
|
||||||
|
} else {
|
||||||
|
return Arrays.asList(new OffStep(vibratorOffTimeout, controller));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DEBUG) {
|
||||||
|
Slog.d(TAG, "Ramping down vibrator " + controller.getVibratorInfo().getId()
|
||||||
|
+ " from amplitude " + currentAmplitude
|
||||||
|
+ " for " + rampDownDuration + "ms");
|
||||||
|
}
|
||||||
|
float amplitudeDelta = currentAmplitude / (rampDownDuration / stepDownDuration);
|
||||||
|
float amplitudeTarget = currentAmplitude - amplitudeDelta;
|
||||||
|
long newVibratorOffTimeout = mCancelled ? rampDownDuration : vibratorOffTimeout;
|
||||||
|
return Arrays.asList(new RampOffStep(startTime, amplitudeTarget, amplitudeDelta,
|
||||||
|
controller, newVibratorOffTimeout));
|
||||||
|
} finally {
|
||||||
|
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Represents a step to ramp down the vibrator amplitude before turning it off. */
|
||||||
|
private final class RampOffStep extends SingleVibratorStep {
|
||||||
|
private final float mAmplitudeTarget;
|
||||||
|
private final float mAmplitudeDelta;
|
||||||
|
|
||||||
|
RampOffStep(long startTime, float amplitudeTarget, float amplitudeDelta,
|
||||||
|
VibratorController controller, long vibratorOffTimeout) {
|
||||||
|
super(startTime, controller, /* effect= */ null, /* index= */ -1, vibratorOffTimeout);
|
||||||
|
mAmplitudeTarget = amplitudeTarget;
|
||||||
|
mAmplitudeDelta = amplitudeDelta;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCleanUp() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Step> cancel() {
|
||||||
|
return Arrays.asList(new OffStep(SystemClock.uptimeMillis(), controller));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Step> play() {
|
||||||
|
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "RampOffStep");
|
||||||
|
try {
|
||||||
|
if (DEBUG) {
|
||||||
|
long latency = SystemClock.uptimeMillis() - startTime;
|
||||||
|
Slog.d(TAG, "Ramp down the vibrator amplitude, step with "
|
||||||
|
+ latency + "ms latency.");
|
||||||
|
}
|
||||||
|
if (mVibratorCallbackReceived) {
|
||||||
|
// Vibration completion callback was received by this step, just turn if off
|
||||||
|
// and skip the rest of the steps to ramp down the vibrator amplitude.
|
||||||
|
stopVibrating();
|
||||||
|
return EMPTY_STEP_LIST;
|
||||||
|
}
|
||||||
|
|
||||||
|
changeAmplitude(mAmplitudeTarget);
|
||||||
|
|
||||||
|
float newAmplitudeTarget = mAmplitudeTarget - mAmplitudeDelta;
|
||||||
|
if (newAmplitudeTarget < RAMP_OFF_AMPLITUDE_MIN) {
|
||||||
|
// Vibrator amplitude cannot go further down, just turn it off.
|
||||||
|
return Arrays.asList(new OffStep(vibratorOffTimeout, controller));
|
||||||
|
}
|
||||||
|
return Arrays.asList(new RampOffStep(
|
||||||
|
startTime + mVibrationSettings.getRampStepDuration(), newAmplitudeTarget,
|
||||||
|
mAmplitudeDelta, controller, vibratorOffTimeout));
|
||||||
|
} finally {
|
||||||
|
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a step to turn the vibrator off.
|
* Represents a step to turn the vibrator off.
|
||||||
*
|
*
|
||||||
@@ -949,6 +1248,21 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
|||||||
super(startTime, controller, /* effect= */ null, /* index= */ -1, startTime);
|
super(startTime, controller, /* effect= */ null, /* index= */ -1, startTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCleanUp() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Step> cancel() {
|
||||||
|
return Arrays.asList(new OffStep(SystemClock.uptimeMillis(), controller));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cancelImmediately() {
|
||||||
|
stopVibrating();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Step> play() {
|
public List<Step> play() {
|
||||||
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "OffStep");
|
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "OffStep");
|
||||||
@@ -1044,14 +1358,6 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
|||||||
return controller.on(duration, mVibration.id);
|
return controller.on(duration, mVibration.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void changeAmplitude(float amplitude) {
|
|
||||||
if (DEBUG) {
|
|
||||||
Slog.d(TAG, "Amplitude changed on vibrator " + controller.getVibratorInfo().getId()
|
|
||||||
+ " to " + amplitude);
|
|
||||||
}
|
|
||||||
controller.setAmplitude(amplitude);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the duration the vibrator will be on for a waveform, starting at {@code startIndex}
|
* Get the duration the vibrator will be on for a waveform, starting at {@code startIndex}
|
||||||
* until the next time it's vibrating amplitude is zero or a different type of segment is
|
* until the next time it's vibrating amplitude is zero or a different type of segment is
|
||||||
@@ -1080,6 +1386,11 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
|||||||
return 1000;
|
return 1000;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (i == segmentCount && effect.getRepeatIndex() < 0) {
|
||||||
|
// Vibration ending at non-zero amplitude, add extra timings to ramp down after
|
||||||
|
// vibration is complete.
|
||||||
|
timing += mVibrationSettings.getRampDownDuration();
|
||||||
|
}
|
||||||
return timing;
|
return timing;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,6 +54,8 @@ final class VibratorController {
|
|||||||
private boolean mIsVibrating;
|
private boolean mIsVibrating;
|
||||||
@GuardedBy("mLock")
|
@GuardedBy("mLock")
|
||||||
private boolean mIsUnderExternalControl;
|
private boolean mIsUnderExternalControl;
|
||||||
|
@GuardedBy("mLock")
|
||||||
|
private float mCurrentAmplitude;
|
||||||
|
|
||||||
/** Listener for vibration completion callbacks from native. */
|
/** Listener for vibration completion callbacks from native. */
|
||||||
public interface OnVibrationCompleteListener {
|
public interface OnVibrationCompleteListener {
|
||||||
@@ -131,6 +133,23 @@ final class VibratorController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current amplitude the device is vibrating.
|
||||||
|
*
|
||||||
|
* <p>This value is set to 1 by the method {@link #on(long, long)}, and can be updated via
|
||||||
|
* {@link #setAmplitude(float)} if called while the device is vibrating.
|
||||||
|
*
|
||||||
|
* <p>If the device is vibrating via any other {@link #on} method then the current amplitude is
|
||||||
|
* unknown and this will return -1.
|
||||||
|
*
|
||||||
|
* <p>If {@link #isVibrating()} is false then this will be zero.
|
||||||
|
*/
|
||||||
|
public float getCurrentAmplitude() {
|
||||||
|
synchronized (mLock) {
|
||||||
|
return mCurrentAmplitude;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Return {@code true} if this vibrator is under external control, false otherwise. */
|
/** Return {@code true} if this vibrator is under external control, false otherwise. */
|
||||||
public boolean isUnderExternalControl() {
|
public boolean isUnderExternalControl() {
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
@@ -192,6 +211,9 @@ final class VibratorController {
|
|||||||
if (mVibratorInfo.hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL)) {
|
if (mVibratorInfo.hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL)) {
|
||||||
mNativeWrapper.setAmplitude(amplitude);
|
mNativeWrapper.setAmplitude(amplitude);
|
||||||
}
|
}
|
||||||
|
if (mIsVibrating) {
|
||||||
|
mCurrentAmplitude = amplitude;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -208,6 +230,7 @@ final class VibratorController {
|
|||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
long duration = mNativeWrapper.on(milliseconds, vibrationId);
|
long duration = mNativeWrapper.on(milliseconds, vibrationId);
|
||||||
if (duration > 0) {
|
if (duration > 0) {
|
||||||
|
mCurrentAmplitude = -1;
|
||||||
notifyVibratorOnLocked();
|
notifyVibratorOnLocked();
|
||||||
}
|
}
|
||||||
return duration;
|
return duration;
|
||||||
@@ -228,6 +251,7 @@ final class VibratorController {
|
|||||||
long duration = mNativeWrapper.perform(prebaked.getEffectId(),
|
long duration = mNativeWrapper.perform(prebaked.getEffectId(),
|
||||||
prebaked.getEffectStrength(), vibrationId);
|
prebaked.getEffectStrength(), vibrationId);
|
||||||
if (duration > 0) {
|
if (duration > 0) {
|
||||||
|
mCurrentAmplitude = -1;
|
||||||
notifyVibratorOnLocked();
|
notifyVibratorOnLocked();
|
||||||
}
|
}
|
||||||
return duration;
|
return duration;
|
||||||
@@ -250,6 +274,7 @@ final class VibratorController {
|
|||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
long duration = mNativeWrapper.compose(primitives, vibrationId);
|
long duration = mNativeWrapper.compose(primitives, vibrationId);
|
||||||
if (duration > 0) {
|
if (duration > 0) {
|
||||||
|
mCurrentAmplitude = -1;
|
||||||
notifyVibratorOnLocked();
|
notifyVibratorOnLocked();
|
||||||
}
|
}
|
||||||
return duration;
|
return duration;
|
||||||
@@ -272,6 +297,7 @@ final class VibratorController {
|
|||||||
int braking = mVibratorInfo.getDefaultBraking();
|
int braking = mVibratorInfo.getDefaultBraking();
|
||||||
long duration = mNativeWrapper.composePwle(primitives, braking, vibrationId);
|
long duration = mNativeWrapper.composePwle(primitives, braking, vibrationId);
|
||||||
if (duration > 0) {
|
if (duration > 0) {
|
||||||
|
mCurrentAmplitude = -1;
|
||||||
notifyVibratorOnLocked();
|
notifyVibratorOnLocked();
|
||||||
}
|
}
|
||||||
return duration;
|
return duration;
|
||||||
@@ -282,20 +308,24 @@ final class VibratorController {
|
|||||||
public void off() {
|
public void off() {
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
mNativeWrapper.off();
|
mNativeWrapper.off();
|
||||||
|
mCurrentAmplitude = 0;
|
||||||
notifyVibratorOffLocked();
|
notifyVibratorOffLocked();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
|
synchronized (mLock) {
|
||||||
return "VibratorController{"
|
return "VibratorController{"
|
||||||
+ "mVibratorInfo=" + mVibratorInfo
|
+ "mVibratorInfo=" + mVibratorInfo
|
||||||
+ ", mIsVibrating=" + mIsVibrating
|
+ ", mIsVibrating=" + mIsVibrating
|
||||||
|
+ ", mCurrentAmplitude=" + mCurrentAmplitude
|
||||||
+ ", mIsUnderExternalControl=" + mIsUnderExternalControl
|
+ ", mIsUnderExternalControl=" + mIsUnderExternalControl
|
||||||
+ ", mVibratorStateListeners count="
|
+ ", mVibratorStateListeners count="
|
||||||
+ mVibratorStateListeners.getRegisteredCallbackCount()
|
+ mVibratorStateListeners.getRegisteredCallbackCount()
|
||||||
+ '}';
|
+ '}';
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@GuardedBy("mLock")
|
@GuardedBy("mLock")
|
||||||
private void notifyVibratorOnLocked() {
|
private void notifyVibratorOnLocked() {
|
||||||
|
|||||||
@@ -514,8 +514,9 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
|
|||||||
return Vibration.Status.FORWARDED_TO_INPUT_DEVICES;
|
return Vibration.Status.FORWARDED_TO_INPUT_DEVICES;
|
||||||
}
|
}
|
||||||
|
|
||||||
VibrationThread vibThread = new VibrationThread(vib, mDeviceVibrationEffectAdapter,
|
VibrationThread vibThread = new VibrationThread(vib, mVibrationSettings,
|
||||||
mVibrators, mWakeLock, mBatteryStatsService, mVibrationCallbacks);
|
mDeviceVibrationEffectAdapter, mVibrators, mWakeLock, mBatteryStatsService,
|
||||||
|
mVibrationCallbacks);
|
||||||
|
|
||||||
if (mCurrentVibration == null) {
|
if (mCurrentVibration == null) {
|
||||||
return startVibrationThreadLocked(vibThread);
|
return startVibrationThreadLocked(vibThread);
|
||||||
@@ -569,7 +570,6 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
|
|||||||
Trace.asyncTraceEnd(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
|
Trace.asyncTraceEnd(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
|
||||||
try {
|
try {
|
||||||
Vibration vib = mCurrentVibration.getVibration();
|
Vibration vib = mCurrentVibration.getVibration();
|
||||||
mCurrentVibration = null;
|
|
||||||
endVibrationLocked(vib, status);
|
endVibrationLocked(vib, status);
|
||||||
finishAppOpModeLocked(vib.uid, vib.opPkg);
|
finishAppOpModeLocked(vib.uid, vib.opPkg);
|
||||||
} finally {
|
} finally {
|
||||||
@@ -613,7 +613,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
|
|||||||
// Repeating vibrations always take precedence.
|
// Repeating vibrations always take precedence.
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (mCurrentVibration != null) {
|
if (mCurrentVibration != null && !mCurrentVibration.getVibration().hasEnded()) {
|
||||||
if (mCurrentVibration.getVibration().attrs.getUsage()
|
if (mCurrentVibration.getVibration().attrs.getUsage()
|
||||||
== VibrationAttributes.USAGE_ALARM) {
|
== VibrationAttributes.USAGE_ALARM) {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
@@ -1003,15 +1003,25 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onVibrationEnded(long vibrationId, Vibration.Status status) {
|
public void onVibrationCompleted(long vibrationId, Vibration.Status status) {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Slog.d(TAG, "Vibration " + vibrationId + " thread finished with status " + status);
|
Slog.d(TAG, "Vibration " + vibrationId + " finished with status " + status);
|
||||||
}
|
}
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
if (mCurrentVibration != null
|
if (mCurrentVibration != null
|
||||||
&& mCurrentVibration.getVibration().id == vibrationId) {
|
&& mCurrentVibration.getVibration().id == vibrationId) {
|
||||||
reportFinishedVibrationLocked(status);
|
reportFinishedVibrationLocked(status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onVibratorsReleased() {
|
||||||
|
if (DEBUG) {
|
||||||
|
Slog.d(TAG, "Vibrators released after finished vibration");
|
||||||
|
}
|
||||||
|
synchronized (mLock) {
|
||||||
|
mCurrentVibration = null;
|
||||||
if (mNextVibration != null) {
|
if (mNextVibration != null) {
|
||||||
VibrationThread vibThread = mNextVibration;
|
VibrationThread vibThread = mNextVibration;
|
||||||
mNextVibration = null;
|
mNextVibration = null;
|
||||||
@@ -1020,7 +1030,6 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/** Listener for synced vibration completion callbacks from native. */
|
/** Listener for synced vibration completion callbacks from native. */
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
@@ -1337,7 +1346,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
|
|||||||
// 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 (mCurrentVibration != null) {
|
if (mCurrentVibration != null) {
|
||||||
mNextVibration = null;
|
mNextVibration = null;
|
||||||
mCurrentVibration.cancel();
|
mCurrentVibration.cancelImmediately();
|
||||||
cancelingVibration = mCurrentVibration;
|
cancelingVibration = mCurrentVibration;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import static org.mockito.ArgumentMatchers.same;
|
|||||||
import static org.mockito.Mockito.doAnswer;
|
import static org.mockito.Mockito.doAnswer;
|
||||||
import static org.mockito.Mockito.inOrder;
|
import static org.mockito.Mockito.inOrder;
|
||||||
import static org.mockito.Mockito.never;
|
import static org.mockito.Mockito.never;
|
||||||
|
import static org.mockito.Mockito.timeout;
|
||||||
import static org.mockito.Mockito.times;
|
import static org.mockito.Mockito.times;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
@@ -86,6 +87,7 @@ public class VibrationThreadTest {
|
|||||||
private static final int VIBRATOR_ID = 1;
|
private static final int VIBRATOR_ID = 1;
|
||||||
private static final String PACKAGE_NAME = "package";
|
private static final String PACKAGE_NAME = "package";
|
||||||
private static final VibrationAttributes ATTRS = new VibrationAttributes.Builder().build();
|
private static final VibrationAttributes ATTRS = new VibrationAttributes.Builder().build();
|
||||||
|
private static final int TEST_RAMP_STEP_DURATION = 5;
|
||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public MockitoRule mMockitoRule = MockitoJUnit.rule();
|
public MockitoRule mMockitoRule = MockitoJUnit.rule();
|
||||||
@@ -100,6 +102,7 @@ public class VibrationThreadTest {
|
|||||||
private IBatteryStats mIBatteryStatsMock;
|
private IBatteryStats mIBatteryStatsMock;
|
||||||
|
|
||||||
private final Map<Integer, FakeVibratorControllerProvider> mVibratorProviders = new HashMap<>();
|
private final Map<Integer, FakeVibratorControllerProvider> mVibratorProviders = new HashMap<>();
|
||||||
|
private VibrationSettings mVibrationSettings;
|
||||||
private DeviceVibrationEffectAdapter mEffectAdapter;
|
private DeviceVibrationEffectAdapter mEffectAdapter;
|
||||||
private PowerManager.WakeLock mWakeLock;
|
private PowerManager.WakeLock mWakeLock;
|
||||||
private TestLooper mTestLooper;
|
private TestLooper mTestLooper;
|
||||||
@@ -109,9 +112,9 @@ public class VibrationThreadTest {
|
|||||||
mTestLooper = new TestLooper();
|
mTestLooper = new TestLooper();
|
||||||
|
|
||||||
Context context = InstrumentationRegistry.getContext();
|
Context context = InstrumentationRegistry.getContext();
|
||||||
VibrationSettings vibrationSettings = new VibrationSettings(context,
|
mVibrationSettings = new VibrationSettings(context, new Handler(mTestLooper.getLooper()),
|
||||||
new Handler(mTestLooper.getLooper()));
|
/* rampDownDuration= */ 0, TEST_RAMP_STEP_DURATION);
|
||||||
mEffectAdapter = new DeviceVibrationEffectAdapter(vibrationSettings);
|
mEffectAdapter = new DeviceVibrationEffectAdapter(mVibrationSettings);
|
||||||
mWakeLock = context.getSystemService(
|
mWakeLock = context.getSystemService(
|
||||||
PowerManager.class).newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*vibrator*");
|
PowerManager.class).newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*vibrator*");
|
||||||
|
|
||||||
@@ -128,8 +131,7 @@ public class VibrationThreadTest {
|
|||||||
waitForCompletion(thread);
|
waitForCompletion(thread);
|
||||||
|
|
||||||
verify(mControllerCallbacks, never()).onComplete(anyInt(), eq(vibrationId));
|
verify(mControllerCallbacks, never()).onComplete(anyInt(), eq(vibrationId));
|
||||||
verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId),
|
verifyCallbacksTriggered(vibrationId, Vibration.Status.IGNORED_UNSUPPORTED);
|
||||||
eq(Vibration.Status.IGNORED_UNSUPPORTED));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -143,8 +145,7 @@ public class VibrationThreadTest {
|
|||||||
waitForCompletion(thread);
|
waitForCompletion(thread);
|
||||||
|
|
||||||
verify(mControllerCallbacks, never()).onComplete(anyInt(), eq(vibrationId));
|
verify(mControllerCallbacks, never()).onComplete(anyInt(), eq(vibrationId));
|
||||||
verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId),
|
verifyCallbacksTriggered(vibrationId, Vibration.Status.IGNORED_UNSUPPORTED);
|
||||||
eq(Vibration.Status.IGNORED_UNSUPPORTED));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -159,7 +160,7 @@ public class VibrationThreadTest {
|
|||||||
verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(10L));
|
verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(10L));
|
||||||
verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
|
verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
|
||||||
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
||||||
verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED));
|
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
||||||
assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
|
assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
|
||||||
|
|
||||||
assertEquals(Arrays.asList(expectedOneShot(10)),
|
assertEquals(Arrays.asList(expectedOneShot(10)),
|
||||||
@@ -178,7 +179,7 @@ public class VibrationThreadTest {
|
|||||||
verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(10L));
|
verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(10L));
|
||||||
verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
|
verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
|
||||||
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
||||||
verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED));
|
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
||||||
assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
|
assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
|
||||||
|
|
||||||
assertEquals(Arrays.asList(expectedOneShot(10)),
|
assertEquals(Arrays.asList(expectedOneShot(10)),
|
||||||
@@ -200,7 +201,7 @@ public class VibrationThreadTest {
|
|||||||
verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(15L));
|
verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(15L));
|
||||||
verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
|
verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
|
||||||
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
||||||
verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED));
|
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
||||||
assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
|
assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
|
||||||
|
|
||||||
assertEquals(Arrays.asList(expectedOneShot(15)),
|
assertEquals(Arrays.asList(expectedOneShot(15)),
|
||||||
@@ -232,7 +233,7 @@ public class VibrationThreadTest {
|
|||||||
|
|
||||||
verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), anyLong());
|
verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), anyLong());
|
||||||
verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
|
verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
|
||||||
verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.CANCELLED));
|
verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
|
||||||
assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
|
assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
|
||||||
|
|
||||||
List<Float> playedAmplitudes = fakeVibrator.getAmplitudes();
|
List<Float> playedAmplitudes = fakeVibrator.getAmplitudes();
|
||||||
@@ -269,7 +270,7 @@ public class VibrationThreadTest {
|
|||||||
waitForCompletion(vibrationThread, /* timeout= */ 50);
|
waitForCompletion(vibrationThread, /* timeout= */ 50);
|
||||||
waitForCompletion(cancellingThread);
|
waitForCompletion(cancellingThread);
|
||||||
|
|
||||||
verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.CANCELLED));
|
verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
|
||||||
assertFalse(vibrationThread.getVibrators().get(VIBRATOR_ID).isVibrating());
|
assertFalse(vibrationThread.getVibrators().get(VIBRATOR_ID).isVibrating());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -294,7 +295,7 @@ public class VibrationThreadTest {
|
|||||||
waitForCompletion(vibrationThread, /* timeout= */ 50);
|
waitForCompletion(vibrationThread, /* timeout= */ 50);
|
||||||
waitForCompletion(cancellingThread);
|
waitForCompletion(cancellingThread);
|
||||||
|
|
||||||
verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.CANCELLED));
|
verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
|
||||||
assertFalse(vibrationThread.getVibrators().get(VIBRATOR_ID).isVibrating());
|
assertFalse(vibrationThread.getVibrators().get(VIBRATOR_ID).isVibrating());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -310,7 +311,7 @@ public class VibrationThreadTest {
|
|||||||
verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(20L));
|
verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(20L));
|
||||||
verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
|
verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
|
||||||
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
||||||
verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED));
|
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
||||||
assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
|
assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
|
||||||
|
|
||||||
assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_THUD)),
|
assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_THUD)),
|
||||||
@@ -333,7 +334,7 @@ public class VibrationThreadTest {
|
|||||||
verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(10L));
|
verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(10L));
|
||||||
verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
|
verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
|
||||||
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
||||||
verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED));
|
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
||||||
assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
|
assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
|
||||||
|
|
||||||
assertEquals(Arrays.asList(expectedOneShot(10)),
|
assertEquals(Arrays.asList(expectedOneShot(10)),
|
||||||
@@ -352,8 +353,7 @@ public class VibrationThreadTest {
|
|||||||
verify(mIBatteryStatsMock, never()).noteVibratorOn(eq(UID), anyLong());
|
verify(mIBatteryStatsMock, never()).noteVibratorOn(eq(UID), anyLong());
|
||||||
verify(mIBatteryStatsMock, never()).noteVibratorOff(eq(UID));
|
verify(mIBatteryStatsMock, never()).noteVibratorOff(eq(UID));
|
||||||
verify(mControllerCallbacks, never()).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
verify(mControllerCallbacks, never()).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
||||||
verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId),
|
verifyCallbacksTriggered(vibrationId, Vibration.Status.IGNORED_UNSUPPORTED);
|
||||||
eq(Vibration.Status.IGNORED_UNSUPPORTED));
|
|
||||||
assertTrue(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments().isEmpty());
|
assertTrue(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments().isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -373,7 +373,7 @@ public class VibrationThreadTest {
|
|||||||
verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(40L));
|
verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(40L));
|
||||||
verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
|
verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
|
||||||
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
||||||
verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED));
|
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
||||||
assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
|
assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
|
||||||
assertEquals(Arrays.asList(
|
assertEquals(Arrays.asList(
|
||||||
expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 0),
|
expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 0),
|
||||||
@@ -393,8 +393,7 @@ public class VibrationThreadTest {
|
|||||||
verify(mIBatteryStatsMock, never()).noteVibratorOn(eq(UID), anyLong());
|
verify(mIBatteryStatsMock, never()).noteVibratorOn(eq(UID), anyLong());
|
||||||
verify(mIBatteryStatsMock, never()).noteVibratorOff(eq(UID));
|
verify(mIBatteryStatsMock, never()).noteVibratorOff(eq(UID));
|
||||||
verify(mControllerCallbacks, never()).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
verify(mControllerCallbacks, never()).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
||||||
verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId),
|
verifyCallbacksTriggered(vibrationId, Vibration.Status.IGNORED_UNSUPPORTED);
|
||||||
eq(Vibration.Status.IGNORED_UNSUPPORTED));
|
|
||||||
assertTrue(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments().isEmpty());
|
assertTrue(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments().isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -413,7 +412,7 @@ public class VibrationThreadTest {
|
|||||||
VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
|
VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
|
||||||
waitForCompletion(thread);
|
waitForCompletion(thread);
|
||||||
|
|
||||||
verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED));
|
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
||||||
// Vibrator compose called twice.
|
// Vibrator compose called twice.
|
||||||
verify(mControllerCallbacks, times(2)).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
verify(mControllerCallbacks, times(2)).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
||||||
assertEquals(3, fakeVibrator.getEffectSegments().size());
|
assertEquals(3, fakeVibrator.getEffectSegments().size());
|
||||||
@@ -443,7 +442,7 @@ public class VibrationThreadTest {
|
|||||||
verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(10L));
|
verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(10L));
|
||||||
verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
|
verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
|
||||||
verify(mControllerCallbacks, times(4)).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
verify(mControllerCallbacks, times(4)).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
||||||
verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED));
|
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
||||||
assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
|
assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
|
||||||
assertEquals(Arrays.asList(
|
assertEquals(Arrays.asList(
|
||||||
expectedOneShot(10),
|
expectedOneShot(10),
|
||||||
@@ -479,7 +478,7 @@ public class VibrationThreadTest {
|
|||||||
verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(100L));
|
verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(100L));
|
||||||
verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
|
verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
|
||||||
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
||||||
verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED));
|
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
||||||
assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
|
assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
|
||||||
assertEquals(Arrays.asList(
|
assertEquals(Arrays.asList(
|
||||||
expectedRamp(/* amplitude= */ 1, /* frequency= */ 150, /* duration= */ 10),
|
expectedRamp(/* amplitude= */ 1, /* frequency= */ 150, /* duration= */ 10),
|
||||||
@@ -512,7 +511,7 @@ public class VibrationThreadTest {
|
|||||||
VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
|
VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
|
||||||
waitForCompletion(thread);
|
waitForCompletion(thread);
|
||||||
|
|
||||||
verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED));
|
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
||||||
// Vibrator compose called twice.
|
// Vibrator compose called twice.
|
||||||
verify(mControllerCallbacks, times(2)).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
verify(mControllerCallbacks, times(2)).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
||||||
assertEquals(4, fakeVibrator.getEffectSegments().size());
|
assertEquals(4, fakeVibrator.getEffectSegments().size());
|
||||||
@@ -537,7 +536,7 @@ public class VibrationThreadTest {
|
|||||||
waitForCompletion(thread);
|
waitForCompletion(thread);
|
||||||
assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
|
assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
|
||||||
|
|
||||||
verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.CANCELLED));
|
verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -545,10 +544,10 @@ public class VibrationThreadTest {
|
|||||||
mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
|
mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
|
||||||
|
|
||||||
long vibrationId = 1;
|
long vibrationId = 1;
|
||||||
waitForCompletion(startThreadAndDispatcher(vibrationId++,
|
waitForCompletion(startThreadAndDispatcher(vibrationId,
|
||||||
VibrationEffect.createOneShot(10, 100)));
|
VibrationEffect.createOneShot(10, 100)));
|
||||||
|
|
||||||
verify(mThreadCallbacks).onVibrationEnded(anyLong(), eq(Vibration.Status.FINISHED));
|
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
||||||
verify(mThreadCallbacks, never()).prepareSyncedVibration(anyLong(), any());
|
verify(mThreadCallbacks, never()).prepareSyncedVibration(anyLong(), any());
|
||||||
verify(mThreadCallbacks, never()).triggerSyncedVibration(anyLong());
|
verify(mThreadCallbacks, never()).triggerSyncedVibration(anyLong());
|
||||||
verify(mThreadCallbacks, never()).cancelSyncedVibration();
|
verify(mThreadCallbacks, never()).cancelSyncedVibration();
|
||||||
@@ -571,7 +570,7 @@ public class VibrationThreadTest {
|
|||||||
verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
|
verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
|
||||||
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
||||||
verify(mControllerCallbacks, never()).onComplete(eq(2), eq(vibrationId));
|
verify(mControllerCallbacks, never()).onComplete(eq(2), eq(vibrationId));
|
||||||
verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED));
|
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
||||||
assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
|
assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
|
||||||
|
|
||||||
assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_TICK)),
|
assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_TICK)),
|
||||||
@@ -596,7 +595,7 @@ public class VibrationThreadTest {
|
|||||||
verify(mControllerCallbacks).onComplete(eq(1), eq(vibrationId));
|
verify(mControllerCallbacks).onComplete(eq(1), eq(vibrationId));
|
||||||
verify(mControllerCallbacks).onComplete(eq(2), eq(vibrationId));
|
verify(mControllerCallbacks).onComplete(eq(2), eq(vibrationId));
|
||||||
verify(mControllerCallbacks).onComplete(eq(3), eq(vibrationId));
|
verify(mControllerCallbacks).onComplete(eq(3), eq(vibrationId));
|
||||||
verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED));
|
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
||||||
assertFalse(thread.getVibrators().get(1).isVibrating());
|
assertFalse(thread.getVibrators().get(1).isVibrating());
|
||||||
assertFalse(thread.getVibrators().get(2).isVibrating());
|
assertFalse(thread.getVibrators().get(2).isVibrating());
|
||||||
assertFalse(thread.getVibrators().get(3).isVibrating());
|
assertFalse(thread.getVibrators().get(3).isVibrating());
|
||||||
@@ -635,7 +634,7 @@ public class VibrationThreadTest {
|
|||||||
verify(mControllerCallbacks).onComplete(eq(2), eq(vibrationId));
|
verify(mControllerCallbacks).onComplete(eq(2), eq(vibrationId));
|
||||||
verify(mControllerCallbacks).onComplete(eq(3), eq(vibrationId));
|
verify(mControllerCallbacks).onComplete(eq(3), eq(vibrationId));
|
||||||
verify(mControllerCallbacks).onComplete(eq(4), eq(vibrationId));
|
verify(mControllerCallbacks).onComplete(eq(4), eq(vibrationId));
|
||||||
verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED));
|
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
||||||
assertFalse(thread.getVibrators().get(1).isVibrating());
|
assertFalse(thread.getVibrators().get(1).isVibrating());
|
||||||
assertFalse(thread.getVibrators().get(2).isVibrating());
|
assertFalse(thread.getVibrators().get(2).isVibrating());
|
||||||
assertFalse(thread.getVibrators().get(3).isVibrating());
|
assertFalse(thread.getVibrators().get(3).isVibrating());
|
||||||
@@ -686,7 +685,7 @@ public class VibrationThreadTest {
|
|||||||
batterVerifier.verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(20L));
|
batterVerifier.verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(20L));
|
||||||
batterVerifier.verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
|
batterVerifier.verify(mIBatteryStatsMock).noteVibratorOff(eq(UID));
|
||||||
|
|
||||||
verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED));
|
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
||||||
assertFalse(thread.getVibrators().get(1).isVibrating());
|
assertFalse(thread.getVibrators().get(1).isVibrating());
|
||||||
assertFalse(thread.getVibrators().get(2).isVibrating());
|
assertFalse(thread.getVibrators().get(2).isVibrating());
|
||||||
assertFalse(thread.getVibrators().get(3).isVibrating());
|
assertFalse(thread.getVibrators().get(3).isVibrating());
|
||||||
@@ -727,7 +726,7 @@ public class VibrationThreadTest {
|
|||||||
verify(mThreadCallbacks).prepareSyncedVibration(eq(expectedCap), eq(vibratorIds));
|
verify(mThreadCallbacks).prepareSyncedVibration(eq(expectedCap), eq(vibratorIds));
|
||||||
verify(mThreadCallbacks).triggerSyncedVibration(eq(vibrationId));
|
verify(mThreadCallbacks).triggerSyncedVibration(eq(vibrationId));
|
||||||
verify(mThreadCallbacks, never()).cancelSyncedVibration();
|
verify(mThreadCallbacks, never()).cancelSyncedVibration();
|
||||||
verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED));
|
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
||||||
|
|
||||||
VibrationEffectSegment expected = expectedPrimitive(
|
VibrationEffectSegment expected = expectedPrimitive(
|
||||||
VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 100);
|
VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 100);
|
||||||
@@ -767,7 +766,7 @@ public class VibrationThreadTest {
|
|||||||
verify(mThreadCallbacks).prepareSyncedVibration(eq(expectedCap), eq(vibratorIds));
|
verify(mThreadCallbacks).prepareSyncedVibration(eq(expectedCap), eq(vibratorIds));
|
||||||
verify(mThreadCallbacks).triggerSyncedVibration(eq(vibrationId));
|
verify(mThreadCallbacks).triggerSyncedVibration(eq(vibrationId));
|
||||||
verify(mThreadCallbacks, never()).cancelSyncedVibration();
|
verify(mThreadCallbacks, never()).cancelSyncedVibration();
|
||||||
verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED));
|
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -858,7 +857,7 @@ public class VibrationThreadTest {
|
|||||||
verify(mControllerCallbacks).onComplete(eq(1), eq(vibrationId));
|
verify(mControllerCallbacks).onComplete(eq(1), eq(vibrationId));
|
||||||
verify(mControllerCallbacks).onComplete(eq(2), eq(vibrationId));
|
verify(mControllerCallbacks).onComplete(eq(2), eq(vibrationId));
|
||||||
verify(mControllerCallbacks).onComplete(eq(3), eq(vibrationId));
|
verify(mControllerCallbacks).onComplete(eq(3), eq(vibrationId));
|
||||||
verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED));
|
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
||||||
assertFalse(thread.getVibrators().get(1).isVibrating());
|
assertFalse(thread.getVibrators().get(1).isVibrating());
|
||||||
assertFalse(thread.getVibrators().get(2).isVibrating());
|
assertFalse(thread.getVibrators().get(2).isVibrating());
|
||||||
assertFalse(thread.getVibrators().get(3).isVibrating());
|
assertFalse(thread.getVibrators().get(3).isVibrating());
|
||||||
@@ -935,7 +934,7 @@ public class VibrationThreadTest {
|
|||||||
|
|
||||||
// After the vibrator call ends the vibration is cancelled and the vibrator is turned off.
|
// After the vibrator call ends the vibration is cancelled and the vibrator is turned off.
|
||||||
waitForCompletion(vibrationThread, /* timeout= */ latency + TEST_TIMEOUT_MILLIS);
|
waitForCompletion(vibrationThread, /* timeout= */ latency + TEST_TIMEOUT_MILLIS);
|
||||||
verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.CANCELLED));
|
verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
|
||||||
assertFalse(vibrationThread.getVibrators().get(VIBRATOR_ID).isVibrating());
|
assertFalse(vibrationThread.getVibrators().get(VIBRATOR_ID).isVibrating());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -968,7 +967,7 @@ public class VibrationThreadTest {
|
|||||||
waitForCompletion(vibrationThread, /* timeout= */ 50);
|
waitForCompletion(vibrationThread, /* timeout= */ 50);
|
||||||
waitForCompletion(cancellingThread);
|
waitForCompletion(cancellingThread);
|
||||||
|
|
||||||
verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.CANCELLED));
|
verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
|
||||||
assertFalse(vibrationThread.getVibrators().get(1).isVibrating());
|
assertFalse(vibrationThread.getVibrators().get(1).isVibrating());
|
||||||
assertFalse(vibrationThread.getVibrators().get(2).isVibrating());
|
assertFalse(vibrationThread.getVibrators().get(2).isVibrating());
|
||||||
}
|
}
|
||||||
@@ -1000,7 +999,7 @@ public class VibrationThreadTest {
|
|||||||
waitForCompletion(vibrationThread, /* timeout= */ 50);
|
waitForCompletion(vibrationThread, /* timeout= */ 50);
|
||||||
waitForCompletion(cancellingThread);
|
waitForCompletion(cancellingThread);
|
||||||
|
|
||||||
verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.CANCELLED));
|
verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
|
||||||
assertFalse(vibrationThread.getVibrators().get(1).isVibrating());
|
assertFalse(vibrationThread.getVibrators().get(1).isVibrating());
|
||||||
assertFalse(vibrationThread.getVibrators().get(2).isVibrating());
|
assertFalse(vibrationThread.getVibrators().get(2).isVibrating());
|
||||||
}
|
}
|
||||||
@@ -1020,11 +1019,179 @@ public class VibrationThreadTest {
|
|||||||
|
|
||||||
verify(mVibrationToken).linkToDeath(same(thread), eq(0));
|
verify(mVibrationToken).linkToDeath(same(thread), eq(0));
|
||||||
verify(mVibrationToken).unlinkToDeath(same(thread), eq(0));
|
verify(mVibrationToken).unlinkToDeath(same(thread), eq(0));
|
||||||
verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.CANCELLED));
|
verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
|
||||||
assertFalse(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments().isEmpty());
|
assertFalse(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments().isEmpty());
|
||||||
assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
|
assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void vibrate_waveformWithRampDown_addsRampDownAfterVibrationCompleted() {
|
||||||
|
int rampDownDuration = 15;
|
||||||
|
mVibrationSettings = new VibrationSettings(InstrumentationRegistry.getContext(),
|
||||||
|
new Handler(mTestLooper.getLooper()), rampDownDuration, TEST_RAMP_STEP_DURATION);
|
||||||
|
mEffectAdapter = new DeviceVibrationEffectAdapter(mVibrationSettings);
|
||||||
|
mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
|
||||||
|
|
||||||
|
long vibrationId = 1;
|
||||||
|
VibrationEffect effect = VibrationEffect.createWaveform(
|
||||||
|
new long[]{5, 5, 5}, new int[]{60, 120, 240}, -1);
|
||||||
|
VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
|
||||||
|
waitForCompletion(thread);
|
||||||
|
|
||||||
|
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
||||||
|
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
||||||
|
|
||||||
|
// Duration extended for 5 + 5 + 5 + 15.
|
||||||
|
assertEquals(Arrays.asList(expectedOneShot(30)),
|
||||||
|
mVibratorProviders.get(VIBRATOR_ID).getEffectSegments());
|
||||||
|
List<Float> amplitudes = mVibratorProviders.get(VIBRATOR_ID).getAmplitudes();
|
||||||
|
assertTrue(amplitudes.size() > 3);
|
||||||
|
assertEquals(expectedAmplitudes(60, 120, 240), amplitudes.subList(0, 3));
|
||||||
|
for (int i = 3; i < amplitudes.size(); i++) {
|
||||||
|
assertTrue(amplitudes.get(i) < amplitudes.get(i - 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void vibrate_waveformWithRampDown_triggersCallbackWhenOriginalVibrationEnds() {
|
||||||
|
int rampDownDuration = 10_000;
|
||||||
|
mVibrationSettings = new VibrationSettings(InstrumentationRegistry.getContext(),
|
||||||
|
new Handler(mTestLooper.getLooper()), rampDownDuration, TEST_RAMP_STEP_DURATION);
|
||||||
|
mEffectAdapter = new DeviceVibrationEffectAdapter(mVibrationSettings);
|
||||||
|
mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
|
||||||
|
|
||||||
|
long vibrationId = 1;
|
||||||
|
VibrationEffect effect = VibrationEffect.createOneShot(10, 200);
|
||||||
|
VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
|
||||||
|
|
||||||
|
// Vibration completed but vibrator not yet released.
|
||||||
|
verify(mThreadCallbacks, timeout(TEST_TIMEOUT_MILLIS)).onVibrationCompleted(eq(vibrationId),
|
||||||
|
eq(Vibration.Status.FINISHED));
|
||||||
|
verify(mThreadCallbacks, never()).onVibratorsReleased();
|
||||||
|
|
||||||
|
// Thread still running ramp down.
|
||||||
|
assertTrue(thread.isAlive());
|
||||||
|
|
||||||
|
// Duration extended for 10 + 10000.
|
||||||
|
assertEquals(Arrays.asList(expectedOneShot(10_010)),
|
||||||
|
mVibratorProviders.get(VIBRATOR_ID).getEffectSegments());
|
||||||
|
|
||||||
|
// Will stop the ramp down right away.
|
||||||
|
thread.cancelImmediately();
|
||||||
|
waitForCompletion(thread);
|
||||||
|
|
||||||
|
// Does not cancel already finished vibration, but releases vibrator.
|
||||||
|
verify(mThreadCallbacks, never()).onVibrationCompleted(eq(vibrationId),
|
||||||
|
eq(Vibration.Status.CANCELLED));
|
||||||
|
verify(mThreadCallbacks).onVibratorsReleased();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void vibrate_waveformCancelledWithRampDown_addsRampDownAfterVibrationCancelled()
|
||||||
|
throws Exception {
|
||||||
|
int rampDownDuration = 15;
|
||||||
|
mVibrationSettings = new VibrationSettings(InstrumentationRegistry.getContext(),
|
||||||
|
new Handler(mTestLooper.getLooper()), rampDownDuration, TEST_RAMP_STEP_DURATION);
|
||||||
|
mEffectAdapter = new DeviceVibrationEffectAdapter(mVibrationSettings);
|
||||||
|
mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
|
||||||
|
|
||||||
|
long vibrationId = 1;
|
||||||
|
VibrationEffect effect = VibrationEffect.createOneShot(10_000, 240);
|
||||||
|
VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
|
||||||
|
assertTrue(waitUntil(t -> t.getVibrators().get(VIBRATOR_ID).isVibrating(), thread,
|
||||||
|
TEST_TIMEOUT_MILLIS));
|
||||||
|
thread.cancel();
|
||||||
|
waitForCompletion(thread);
|
||||||
|
|
||||||
|
verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED);
|
||||||
|
|
||||||
|
// Duration extended for 10000 + 15.
|
||||||
|
assertEquals(Arrays.asList(expectedOneShot(10_015)),
|
||||||
|
mVibratorProviders.get(VIBRATOR_ID).getEffectSegments());
|
||||||
|
List<Float> amplitudes = mVibratorProviders.get(VIBRATOR_ID).getAmplitudes();
|
||||||
|
assertTrue(amplitudes.size() > 1);
|
||||||
|
for (int i = 1; i < amplitudes.size(); i++) {
|
||||||
|
assertTrue(amplitudes.get(i) < amplitudes.get(i - 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void vibrate_predefinedWithRampDown_doesNotAddRampDown() {
|
||||||
|
int rampDownDuration = 15;
|
||||||
|
mVibrationSettings = new VibrationSettings(InstrumentationRegistry.getContext(),
|
||||||
|
new Handler(mTestLooper.getLooper()), rampDownDuration, TEST_RAMP_STEP_DURATION);
|
||||||
|
mEffectAdapter = new DeviceVibrationEffectAdapter(mVibrationSettings);
|
||||||
|
mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
|
||||||
|
mVibratorProviders.get(VIBRATOR_ID).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
|
||||||
|
|
||||||
|
long vibrationId = 1;
|
||||||
|
VibrationEffect effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
|
||||||
|
VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
|
||||||
|
waitForCompletion(thread);
|
||||||
|
|
||||||
|
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
||||||
|
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
||||||
|
|
||||||
|
assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_CLICK)),
|
||||||
|
mVibratorProviders.get(VIBRATOR_ID).getEffectSegments());
|
||||||
|
assertTrue(mVibratorProviders.get(VIBRATOR_ID).getAmplitudes().isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void vibrate_composedWithRampDown_doesNotAddRampDown() {
|
||||||
|
int rampDownDuration = 15;
|
||||||
|
mVibrationSettings = new VibrationSettings(InstrumentationRegistry.getContext(),
|
||||||
|
new Handler(mTestLooper.getLooper()), rampDownDuration, TEST_RAMP_STEP_DURATION);
|
||||||
|
mEffectAdapter = new DeviceVibrationEffectAdapter(mVibrationSettings);
|
||||||
|
mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL,
|
||||||
|
IVibrator.CAP_COMPOSE_EFFECTS);
|
||||||
|
mVibratorProviders.get(VIBRATOR_ID).setSupportedPrimitives(
|
||||||
|
VibrationEffect.Composition.PRIMITIVE_CLICK);
|
||||||
|
|
||||||
|
long vibrationId = 1;
|
||||||
|
VibrationEffect effect = VibrationEffect.startComposition()
|
||||||
|
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
|
||||||
|
.compose();
|
||||||
|
VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
|
||||||
|
waitForCompletion(thread);
|
||||||
|
|
||||||
|
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
||||||
|
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
Arrays.asList(expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 0)),
|
||||||
|
mVibratorProviders.get(VIBRATOR_ID).getEffectSegments());
|
||||||
|
assertTrue(mVibratorProviders.get(VIBRATOR_ID).getAmplitudes().isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void vibrate_pwleWithRampDown_doesNotAddRampDown() {
|
||||||
|
int rampDownDuration = 15;
|
||||||
|
mVibrationSettings = new VibrationSettings(InstrumentationRegistry.getContext(),
|
||||||
|
new Handler(mTestLooper.getLooper()), rampDownDuration, TEST_RAMP_STEP_DURATION);
|
||||||
|
mEffectAdapter = new DeviceVibrationEffectAdapter(mVibrationSettings);
|
||||||
|
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
|
||||||
|
fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL,
|
||||||
|
IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
|
||||||
|
fakeVibrator.setMinFrequency(100);
|
||||||
|
fakeVibrator.setResonantFrequency(150);
|
||||||
|
fakeVibrator.setFrequencyResolution(50);
|
||||||
|
fakeVibrator.setMaxAmplitudes(1, 1, 1);
|
||||||
|
fakeVibrator.setPwleSizeMax(2);
|
||||||
|
|
||||||
|
long vibrationId = 1;
|
||||||
|
VibrationEffect effect = VibrationEffect.startWaveform().addRamp(1, 1).build();
|
||||||
|
VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
|
||||||
|
waitForCompletion(thread);
|
||||||
|
|
||||||
|
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
||||||
|
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
||||||
|
|
||||||
|
assertEquals(Arrays.asList(expectedRamp(0, 1, 150, 150, 1)),
|
||||||
|
fakeVibrator.getEffectSegments());
|
||||||
|
assertTrue(fakeVibrator.getAmplitudes().isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
private void mockVibrators(int... vibratorIds) {
|
private void mockVibrators(int... vibratorIds) {
|
||||||
for (int vibratorId : vibratorIds) {
|
for (int vibratorId : vibratorIds) {
|
||||||
mVibratorProviders.put(vibratorId,
|
mVibratorProviders.put(vibratorId,
|
||||||
@@ -1042,7 +1209,7 @@ public class VibrationThreadTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private VibrationThread startThreadAndDispatcher(Vibration vib) {
|
private VibrationThread startThreadAndDispatcher(Vibration vib) {
|
||||||
VibrationThread thread = new VibrationThread(vib, mEffectAdapter,
|
VibrationThread thread = new VibrationThread(vib, mVibrationSettings, mEffectAdapter,
|
||||||
createVibratorControllers(), mWakeLock, mIBatteryStatsMock, mThreadCallbacks);
|
createVibratorControllers(), mWakeLock, mIBatteryStatsMock, mThreadCallbacks);
|
||||||
doAnswer(answer -> {
|
doAnswer(answer -> {
|
||||||
thread.vibratorComplete(answer.getArgument(0));
|
thread.vibratorComplete(answer.getArgument(0));
|
||||||
@@ -1117,4 +1284,9 @@ public class VibrationThreadTest {
|
|||||||
.mapToObj(amplitude -> amplitude / 255f)
|
.mapToObj(amplitude -> amplitude / 255f)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void verifyCallbacksTriggered(long vibrationId, Vibration.Status expectedStatus) {
|
||||||
|
verify(mThreadCallbacks).onVibrationCompleted(eq(vibrationId), eq(expectedStatus));
|
||||||
|
verify(mThreadCallbacks).onVibratorsReleased();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ import android.os.VibratorInfo;
|
|||||||
import android.os.test.TestLooper;
|
import android.os.test.TestLooper;
|
||||||
import android.os.vibrator.PrebakedSegment;
|
import android.os.vibrator.PrebakedSegment;
|
||||||
import android.os.vibrator.PrimitiveSegment;
|
import android.os.vibrator.PrimitiveSegment;
|
||||||
|
import android.os.vibrator.VibrationEffectSegment;
|
||||||
import android.platform.test.annotations.Presubmit;
|
import android.platform.test.annotations.Presubmit;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.view.InputDevice;
|
import android.view.InputDevice;
|
||||||
@@ -470,13 +471,14 @@ public class VibratorManagerServiceTest {
|
|||||||
mockVibrators(1);
|
mockVibrators(1);
|
||||||
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1);
|
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1);
|
||||||
mVibrator.setDefaultRingVibrationIntensity(Vibrator.VIBRATION_INTENSITY_MEDIUM);
|
mVibrator.setDefaultRingVibrationIntensity(Vibrator.VIBRATION_INTENSITY_MEDIUM);
|
||||||
fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
|
fakeVibrator.setSupportedEffects(VibrationEffect.EFFECT_CLICK,
|
||||||
|
VibrationEffect.EFFECT_HEAVY_CLICK, VibrationEffect.EFFECT_DOUBLE_CLICK);
|
||||||
|
|
||||||
setRingerMode(AudioManager.RINGER_MODE_NORMAL);
|
setRingerMode(AudioManager.RINGER_MODE_NORMAL);
|
||||||
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
|
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
|
||||||
setGlobalSetting(Settings.Global.APPLY_RAMPING_RINGER, 0);
|
setGlobalSetting(Settings.Global.APPLY_RAMPING_RINGER, 0);
|
||||||
VibratorManagerService service = createSystemReadyService();
|
VibratorManagerService service = createSystemReadyService();
|
||||||
vibrate(service, VibrationEffect.createOneShot(1, 1), RINGTONE_ATTRS);
|
vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), RINGTONE_ATTRS);
|
||||||
// Wait before checking it never played.
|
// Wait before checking it never played.
|
||||||
assertFalse(waitUntil(s -> !fakeVibrator.getEffectSegments().isEmpty(),
|
assertFalse(waitUntil(s -> !fakeVibrator.getEffectSegments().isEmpty(),
|
||||||
service, /* timeout= */ 50));
|
service, /* timeout= */ 50));
|
||||||
@@ -484,43 +486,52 @@ public class VibratorManagerServiceTest {
|
|||||||
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
|
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
|
||||||
setGlobalSetting(Settings.Global.APPLY_RAMPING_RINGER, 1);
|
setGlobalSetting(Settings.Global.APPLY_RAMPING_RINGER, 1);
|
||||||
service = createSystemReadyService();
|
service = createSystemReadyService();
|
||||||
vibrate(service, VibrationEffect.createOneShot(1, 10), RINGTONE_ATTRS);
|
vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK), RINGTONE_ATTRS);
|
||||||
assertTrue(waitUntil(s -> fakeVibrator.getEffectSegments().size() == 1,
|
assertTrue(waitUntil(s -> fakeVibrator.getEffectSegments().size() == 1,
|
||||||
service, TEST_TIMEOUT_MILLIS));
|
service, TEST_TIMEOUT_MILLIS));
|
||||||
|
|
||||||
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1);
|
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1);
|
||||||
setGlobalSetting(Settings.Global.APPLY_RAMPING_RINGER, 0);
|
setGlobalSetting(Settings.Global.APPLY_RAMPING_RINGER, 0);
|
||||||
service = createSystemReadyService();
|
service = createSystemReadyService();
|
||||||
vibrate(service, VibrationEffect.createOneShot(1, 100), RINGTONE_ATTRS);
|
vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK), RINGTONE_ATTRS);
|
||||||
assertTrue(waitUntil(s -> fakeVibrator.getEffectSegments().size() == 2,
|
assertTrue(waitUntil(s -> fakeVibrator.getEffectSegments().size() == 2,
|
||||||
service, TEST_TIMEOUT_MILLIS));
|
service, TEST_TIMEOUT_MILLIS));
|
||||||
|
|
||||||
assertEquals(Arrays.asList(10 / 255f, 100 / 255f),
|
assertEquals(
|
||||||
mVibratorProviders.get(1).getAmplitudes());
|
Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_HEAVY_CLICK),
|
||||||
|
expectedPrebaked(VibrationEffect.EFFECT_DOUBLE_CLICK)),
|
||||||
|
mVibratorProviders.get(1).getEffectSegments());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void vibrate_withPowerMode_usesPowerModeState() throws Exception {
|
public void vibrate_withPowerMode_usesPowerModeState() throws Exception {
|
||||||
mockVibrators(1);
|
mockVibrators(1);
|
||||||
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1);
|
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1);
|
||||||
fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
|
fakeVibrator.setSupportedEffects(VibrationEffect.EFFECT_TICK, VibrationEffect.EFFECT_CLICK,
|
||||||
|
VibrationEffect.EFFECT_HEAVY_CLICK, VibrationEffect.EFFECT_DOUBLE_CLICK);
|
||||||
VibratorManagerService service = createSystemReadyService();
|
VibratorManagerService service = createSystemReadyService();
|
||||||
mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
|
mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
|
||||||
vibrate(service, VibrationEffect.createOneShot(1, 1), HAPTIC_FEEDBACK_ATTRS);
|
vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_TICK), HAPTIC_FEEDBACK_ATTRS);
|
||||||
vibrate(service, VibrationEffect.createOneShot(2, 2), RINGTONE_ATTRS);
|
vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), RINGTONE_ATTRS);
|
||||||
assertTrue(waitUntil(s -> fakeVibrator.getEffectSegments().size() == 1,
|
assertTrue(waitUntil(s -> fakeVibrator.getEffectSegments().size() == 1,
|
||||||
service, TEST_TIMEOUT_MILLIS));
|
service, TEST_TIMEOUT_MILLIS));
|
||||||
|
|
||||||
mRegisteredPowerModeListener.onLowPowerModeChanged(NORMAL_POWER_STATE);
|
mRegisteredPowerModeListener.onLowPowerModeChanged(NORMAL_POWER_STATE);
|
||||||
vibrate(service, VibrationEffect.createOneShot(3, 3), /* attributes= */ null);
|
vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK),
|
||||||
|
/* attrs= */ null);
|
||||||
assertTrue(waitUntil(s -> fakeVibrator.getEffectSegments().size() == 2,
|
assertTrue(waitUntil(s -> fakeVibrator.getEffectSegments().size() == 2,
|
||||||
service, TEST_TIMEOUT_MILLIS));
|
service, TEST_TIMEOUT_MILLIS));
|
||||||
|
|
||||||
vibrate(service, VibrationEffect.createOneShot(4, 4), NOTIFICATION_ATTRS);
|
vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK),
|
||||||
|
NOTIFICATION_ATTRS);
|
||||||
assertTrue(waitUntil(s -> fakeVibrator.getEffectSegments().size() == 3,
|
assertTrue(waitUntil(s -> fakeVibrator.getEffectSegments().size() == 3,
|
||||||
service, TEST_TIMEOUT_MILLIS));
|
service, TEST_TIMEOUT_MILLIS));
|
||||||
|
|
||||||
assertEquals(Arrays.asList(2 / 255f, 3 / 255f, 4 / 255f), fakeVibrator.getAmplitudes());
|
assertEquals(
|
||||||
|
Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_CLICK),
|
||||||
|
expectedPrebaked(VibrationEffect.EFFECT_HEAVY_CLICK),
|
||||||
|
expectedPrebaked(VibrationEffect.EFFECT_DOUBLE_CLICK)),
|
||||||
|
mVibratorProviders.get(1).getEffectSegments());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -836,7 +847,6 @@ public class VibratorManagerServiceTest {
|
|||||||
vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), RINGTONE_ATTRS);
|
vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), RINGTONE_ATTRS);
|
||||||
|
|
||||||
assertEquals(4, fakeVibrator.getEffectSegments().size());
|
assertEquals(4, fakeVibrator.getEffectSegments().size());
|
||||||
assertEquals(1, fakeVibrator.getAmplitudes().size());
|
|
||||||
|
|
||||||
// Notification vibrations will be scaled with SCALE_VERY_HIGH.
|
// Notification vibrations will be scaled with SCALE_VERY_HIGH.
|
||||||
assertTrue(0.6 < fakeVibrator.getAmplitudes().get(0));
|
assertTrue(0.6 < fakeVibrator.getAmplitudes().get(0));
|
||||||
@@ -957,6 +967,10 @@ public class VibratorManagerServiceTest {
|
|||||||
assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
|
assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private VibrationEffectSegment expectedPrebaked(int effectId) {
|
||||||
|
return new PrebakedSegment(effectId, false, VibrationEffect.EFFECT_STRENGTH_MEDIUM);
|
||||||
|
}
|
||||||
|
|
||||||
private void mockCapabilities(long... capabilities) {
|
private void mockCapabilities(long... capabilities) {
|
||||||
when(mNativeWrapperMock.getCapabilities()).thenReturn(
|
when(mNativeWrapperMock.getCapabilities()).thenReturn(
|
||||||
Arrays.stream(capabilities).reduce(0, (a, b) -> a | b));
|
Arrays.stream(capabilities).reduce(0, (a, b) -> a | b));
|
||||||
|
|||||||
Reference in New Issue
Block a user