Merge "Make VibrationThread long-lived." into tm-dev am: 159518e2a5
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/17036647 Change-Id: Ibeb8cc4caeb137d334ba6ab812f2954ea669576d
This commit is contained in:
@@ -49,6 +49,8 @@ final class Vibration {
|
|||||||
FORWARDED_TO_INPUT_DEVICES,
|
FORWARDED_TO_INPUT_DEVICES,
|
||||||
CANCELLED,
|
CANCELLED,
|
||||||
IGNORED_ERROR_APP_OPS,
|
IGNORED_ERROR_APP_OPS,
|
||||||
|
IGNORED_ERROR_CANCELLING,
|
||||||
|
IGNORED_ERROR_SCHEDULING,
|
||||||
IGNORED_ERROR_TOKEN,
|
IGNORED_ERROR_TOKEN,
|
||||||
IGNORED,
|
IGNORED,
|
||||||
IGNORED_APP_OPS,
|
IGNORED_APP_OPS,
|
||||||
|
|||||||
@@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package com.android.server.vibrator;
|
package com.android.server.vibrator;
|
||||||
|
|
||||||
|
import android.annotation.NonNull;
|
||||||
|
import android.annotation.Nullable;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.PowerManager;
|
import android.os.PowerManager;
|
||||||
import android.os.Process;
|
import android.os.Process;
|
||||||
@@ -23,9 +25,12 @@ import android.os.RemoteException;
|
|||||||
import android.os.Trace;
|
import android.os.Trace;
|
||||||
import android.os.WorkSource;
|
import android.os.WorkSource;
|
||||||
import android.util.Slog;
|
import android.util.Slog;
|
||||||
import android.util.SparseArray;
|
|
||||||
|
import com.android.internal.annotations.GuardedBy;
|
||||||
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
|
|
||||||
import java.util.NoSuchElementException;
|
import java.util.NoSuchElementException;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
/** Plays a {@link Vibration} in dedicated thread. */
|
/** Plays a {@link Vibration} in dedicated thread. */
|
||||||
final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
||||||
@@ -76,31 +81,41 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
|||||||
* Tells the manager that the VibrationThread is finished with the previous vibration and
|
* Tells the manager that the VibrationThread is finished with the previous vibration and
|
||||||
* all of its cleanup tasks, and the vibrators can now be used for another vibration.
|
* all of its cleanup tasks, and the vibrators can now be used for another vibration.
|
||||||
*/
|
*/
|
||||||
void onVibrationThreadReleased();
|
void onVibrationThreadReleased(long vibrationId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private final PowerManager.WakeLock mWakeLock;
|
private final PowerManager.WakeLock mWakeLock;
|
||||||
private final VibrationThread.VibratorManagerHooks mVibratorManagerHooks;
|
private final VibrationThread.VibratorManagerHooks mVibratorManagerHooks;
|
||||||
|
|
||||||
private final VibrationStepConductor mStepConductor;
|
// mLock is used here to communicate that the thread's work status has changed. The
|
||||||
|
// VibrationThread is expected to wait until work arrives, and other threads may wait until
|
||||||
|
// work has finished. Therefore, any changes to the conductor must be followed by a notifyAll
|
||||||
|
// so that threads check if their desired state is achieved.
|
||||||
|
private final Object mLock = new Object();
|
||||||
|
|
||||||
private volatile boolean mStop;
|
/**
|
||||||
private volatile boolean mForceStop;
|
* The conductor that is intended to be active. Null value means that a new conductor can
|
||||||
// Variable only set and read in main thread.
|
* be set to run. Note that this field is only reset to null when mExecutingConductor has
|
||||||
|
* completed, so the two fields should be in sync.
|
||||||
|
*/
|
||||||
|
@GuardedBy("mLock")
|
||||||
|
@Nullable
|
||||||
|
private VibrationStepConductor mRequestedActiveConductor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The conductor being executed by this thread, should only be accessed within this thread's
|
||||||
|
* execution. i.e. not thread-safe. {@link #mRequestedActiveConductor} is for cross-thread
|
||||||
|
* signalling.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
private VibrationStepConductor mExecutingConductor;
|
||||||
|
|
||||||
|
// Variable only set and read in main thread, no need to lock.
|
||||||
private boolean mCalledVibrationCompleteCallback = false;
|
private boolean mCalledVibrationCompleteCallback = false;
|
||||||
|
|
||||||
VibrationThread(Vibration vib, VibrationSettings vibrationSettings,
|
VibrationThread(PowerManager.WakeLock wakeLock, VibratorManagerHooks vibratorManagerHooks) {
|
||||||
DeviceVibrationEffectAdapter effectAdapter,
|
|
||||||
SparseArray<VibratorController> availableVibrators, PowerManager.WakeLock wakeLock,
|
|
||||||
VibratorManagerHooks vibratorManagerHooks) {
|
|
||||||
mVibratorManagerHooks = vibratorManagerHooks;
|
|
||||||
mWakeLock = wakeLock;
|
mWakeLock = wakeLock;
|
||||||
mStepConductor = new VibrationStepConductor(vib, vibrationSettings, effectAdapter,
|
mVibratorManagerHooks = vibratorManagerHooks;
|
||||||
availableVibrators, vibratorManagerHooks);
|
|
||||||
}
|
|
||||||
|
|
||||||
Vibration getVibration() {
|
|
||||||
return mStepConductor.getVibration();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -108,32 +123,138 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
|||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Slog.d(TAG, "Binder died, cancelling vibration...");
|
Slog.d(TAG, "Binder died, cancelling vibration...");
|
||||||
}
|
}
|
||||||
mStepConductor.notifyCancelled(/* immediate= */ false);
|
// The binder death link only exists while the conductor is set.
|
||||||
|
// TODO: move the death linking to be associated with the conductor directly, this
|
||||||
|
// awkwardness will go away.
|
||||||
|
VibrationStepConductor conductor;
|
||||||
|
synchronized (mLock) {
|
||||||
|
conductor = mRequestedActiveConductor;
|
||||||
|
}
|
||||||
|
if (conductor != null) {
|
||||||
|
conductor.notifyCancelled(/* immediate= */ false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets/activates the current vibration. Must only be called after receiving
|
||||||
|
* onVibratorsReleased from the previous vibration.
|
||||||
|
*
|
||||||
|
* @return false if VibrationThread couldn't accept it, which shouldn't happen unless called
|
||||||
|
* before the release callback.
|
||||||
|
*/
|
||||||
|
boolean runVibrationOnVibrationThread(VibrationStepConductor conductor) {
|
||||||
|
synchronized (mLock) {
|
||||||
|
if (mRequestedActiveConductor != null) {
|
||||||
|
Slog.wtf(TAG, "Attempt to start vibration when one already running");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
mRequestedActiveConductor = conductor;
|
||||||
|
mLock.notifyAll();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
// Structured to guarantee the vibrators completed and released callbacks at the end of
|
|
||||||
// thread execution. Both of these callbacks are exclusively called from this thread.
|
|
||||||
try {
|
|
||||||
try {
|
|
||||||
Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_DISPLAY);
|
Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_DISPLAY);
|
||||||
runWithWakeLock();
|
while (true) {
|
||||||
} finally {
|
// mExecutingConductor is only modified in this loop.
|
||||||
clientVibrationCompleteIfNotAlready(Vibration.Status.FINISHED_UNEXPECTED);
|
mExecutingConductor = Objects.requireNonNull(waitForVibrationRequest());
|
||||||
|
|
||||||
|
mCalledVibrationCompleteCallback = false;
|
||||||
|
runCurrentVibrationWithWakeLock();
|
||||||
|
if (!mExecutingConductor.isFinished()) {
|
||||||
|
Slog.wtf(TAG, "VibrationThread terminated with unfinished vibration");
|
||||||
}
|
}
|
||||||
} finally {
|
synchronized (mLock) {
|
||||||
mVibratorManagerHooks.onVibrationThreadReleased();
|
// Allow another vibration to be requested.
|
||||||
|
mRequestedActiveConductor = null;
|
||||||
|
}
|
||||||
|
// The callback is run without holding the lock, as it may initiate another vibration.
|
||||||
|
// It's safe to notify even if mVibratorConductor has been re-written, as the "wait"
|
||||||
|
// methods all verify their waited state before returning. In reality though, if the
|
||||||
|
// manager is waiting for the thread to finish, then there is no pending vibration
|
||||||
|
// for this thread.
|
||||||
|
// No point doing this in finally, as if there's an exception, this thread will die
|
||||||
|
// and be unusable anyway.
|
||||||
|
mVibratorManagerHooks.onVibrationThreadReleased(mExecutingConductor.getVibration().id);
|
||||||
|
synchronized (mLock) {
|
||||||
|
mLock.notifyAll();
|
||||||
|
}
|
||||||
|
mExecutingConductor = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Waits until the VibrationThread has finished processing, timing out after the given
|
||||||
|
* number of milliseconds. In general, external locking will manage the ordering of this
|
||||||
|
* with calls to {@link #runVibrationOnVibrationThread}.
|
||||||
|
*
|
||||||
|
* @return true if the vibration completed, or false if waiting timed out.
|
||||||
|
*/
|
||||||
|
public boolean waitForThreadIdle(long maxWaitMillis) {
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
long deadline = now + maxWaitMillis;
|
||||||
|
synchronized (mLock) {
|
||||||
|
while (true) {
|
||||||
|
if (mRequestedActiveConductor == null) {
|
||||||
|
return true; // Done
|
||||||
|
}
|
||||||
|
if (now >= deadline) { // Note that thread.wait(0) waits indefinitely.
|
||||||
|
return false; // Timed out.
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
mLock.wait(deadline - now);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Slog.w(TAG, "VibrationThread interrupted waiting to stop, continuing");
|
||||||
|
}
|
||||||
|
now = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Waits for a signal indicating a vibration is ready to run, then returns its conductor. */
|
||||||
|
@NonNull
|
||||||
|
private VibrationStepConductor waitForVibrationRequest() {
|
||||||
|
while (true) {
|
||||||
|
synchronized (mLock) {
|
||||||
|
if (mRequestedActiveConductor != null) {
|
||||||
|
return mRequestedActiveConductor;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
mLock.wait();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Slog.w(TAG, "VibrationThread interrupted waiting to start, continuing");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only for testing: this method relies on the requested-active conductor, rather than
|
||||||
|
* the executing conductor that's not intended for other threads.
|
||||||
|
*
|
||||||
|
* @return true if the vibration that's currently desired to be active has the given id.
|
||||||
|
*/
|
||||||
|
@VisibleForTesting
|
||||||
|
boolean isRunningVibrationId(long id) {
|
||||||
|
synchronized (mLock) {
|
||||||
|
return (mRequestedActiveConductor != null
|
||||||
|
&& mRequestedActiveConductor.getVibration().id == id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Runs the VibrationThread ensuring that the wake lock is acquired and released. */
|
/** Runs the VibrationThread ensuring that the wake lock is acquired and released. */
|
||||||
private void runWithWakeLock() {
|
private void runCurrentVibrationWithWakeLock() {
|
||||||
WorkSource workSource = new WorkSource(mStepConductor.getVibration().uid);
|
WorkSource workSource = new WorkSource(mExecutingConductor.getVibration().uid);
|
||||||
mWakeLock.setWorkSource(workSource);
|
mWakeLock.setWorkSource(workSource);
|
||||||
mWakeLock.acquire();
|
mWakeLock.acquire();
|
||||||
try {
|
try {
|
||||||
runWithWakeLockAndDeathLink();
|
try {
|
||||||
|
runCurrentVibrationWithWakeLockAndDeathLink();
|
||||||
|
} finally {
|
||||||
|
clientVibrationCompleteIfNotAlready(Vibration.Status.FINISHED_UNEXPECTED);
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
mWakeLock.release();
|
mWakeLock.release();
|
||||||
mWakeLock.setWorkSource(null);
|
mWakeLock.setWorkSource(null);
|
||||||
@@ -144,8 +265,8 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
|||||||
* Runs the VibrationThread with the binder death link, handling link/unlink failures.
|
* Runs the VibrationThread with the binder death link, handling link/unlink failures.
|
||||||
* Called from within runWithWakeLock.
|
* Called from within runWithWakeLock.
|
||||||
*/
|
*/
|
||||||
private void runWithWakeLockAndDeathLink() {
|
private void runCurrentVibrationWithWakeLockAndDeathLink() {
|
||||||
IBinder vibrationBinderToken = mStepConductor.getVibration().token;
|
IBinder vibrationBinderToken = mExecutingConductor.getVibration().token;
|
||||||
try {
|
try {
|
||||||
vibrationBinderToken.linkToDeath(this, 0);
|
vibrationBinderToken.linkToDeath(this, 0);
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
@@ -166,26 +287,6 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Cancel current vibration and ramp down the vibrators gracefully. */
|
|
||||||
public void cancel() {
|
|
||||||
mStepConductor.notifyCancelled(/* immediate= */ false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Cancel current vibration and shuts off the vibrators immediately. */
|
|
||||||
public void cancelImmediately() {
|
|
||||||
mStepConductor.notifyCancelled(/* immediate= */ true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Notify current vibration that a synced step has completed. */
|
|
||||||
public void syncedVibrationComplete() {
|
|
||||||
mStepConductor.notifySyncedVibrationComplete();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Notify current vibration that a step has completed on given vibrator. */
|
|
||||||
public void vibratorComplete(int vibratorId) {
|
|
||||||
mStepConductor.notifyVibratorComplete(vibratorId);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Indicate that the vibration is complete. This can be called multiple times only for
|
// Indicate that the vibration is complete. This can be called multiple times only for
|
||||||
// convenience of handling error conditions - an error after the client is complete won't
|
// convenience of handling error conditions - an error after the client is complete won't
|
||||||
// affect the status.
|
// affect the status.
|
||||||
@@ -193,16 +294,16 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
|||||||
if (!mCalledVibrationCompleteCallback) {
|
if (!mCalledVibrationCompleteCallback) {
|
||||||
mCalledVibrationCompleteCallback = true;
|
mCalledVibrationCompleteCallback = true;
|
||||||
mVibratorManagerHooks.onVibrationCompleted(
|
mVibratorManagerHooks.onVibrationCompleted(
|
||||||
mStepConductor.getVibration().id, completedStatus);
|
mExecutingConductor.getVibration().id, completedStatus);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void playVibration() {
|
private void playVibration() {
|
||||||
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "playVibration");
|
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "playVibration");
|
||||||
try {
|
try {
|
||||||
mStepConductor.prepareToStart();
|
mExecutingConductor.prepareToStart();
|
||||||
while (!mStepConductor.isFinished()) {
|
while (!mExecutingConductor.isFinished()) {
|
||||||
boolean readyToRun = mStepConductor.waitUntilNextStepIsDue();
|
boolean readyToRun = mExecutingConductor.waitUntilNextStepIsDue();
|
||||||
// If we waited, don't run the next step, but instead re-evaluate status.
|
// If we waited, don't run the next step, but instead re-evaluate status.
|
||||||
if (readyToRun) {
|
if (readyToRun) {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
@@ -210,10 +311,10 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
|
|||||||
}
|
}
|
||||||
// Run the step without holding the main lock, to avoid HAL interactions from
|
// Run the step without holding the main lock, to avoid HAL interactions from
|
||||||
// blocking the thread.
|
// blocking the thread.
|
||||||
mStepConductor.runNextStep();
|
mExecutingConductor.runNextStep();
|
||||||
}
|
}
|
||||||
|
|
||||||
Vibration.Status status = mStepConductor.calculateVibrationStatus();
|
Vibration.Status status = mExecutingConductor.calculateVibrationStatus();
|
||||||
// This block can only run once due to mCalledVibrationCompleteCallback.
|
// This block can only run once due to mCalledVibrationCompleteCallback.
|
||||||
if (status != Vibration.Status.RUNNING && !mCalledVibrationCompleteCallback) {
|
if (status != Vibration.Status.RUNNING && !mCalledVibrationCompleteCallback) {
|
||||||
// First time vibration stopped running, start clean-up tasks and notify
|
// First time vibration stopped running, start clean-up tasks and notify
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import android.content.pm.PackageManager;
|
|||||||
import android.hardware.vibrator.IVibrator;
|
import android.hardware.vibrator.IVibrator;
|
||||||
import android.os.BatteryStats;
|
import android.os.BatteryStats;
|
||||||
import android.os.Binder;
|
import android.os.Binder;
|
||||||
|
import android.os.Build;
|
||||||
import android.os.CombinedVibration;
|
import android.os.CombinedVibration;
|
||||||
import android.os.ExternalVibration;
|
import android.os.ExternalVibration;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
@@ -52,6 +53,7 @@ import android.os.VibrationEffect;
|
|||||||
import android.os.VibratorInfo;
|
import android.os.VibratorInfo;
|
||||||
import android.os.vibrator.PrebakedSegment;
|
import android.os.vibrator.PrebakedSegment;
|
||||||
import android.os.vibrator.VibrationEffectSegment;
|
import android.os.vibrator.VibrationEffectSegment;
|
||||||
|
import android.text.TextUtils;
|
||||||
import android.util.Slog;
|
import android.util.Slog;
|
||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
import android.util.proto.ProtoOutputStream;
|
import android.util.proto.ProtoOutputStream;
|
||||||
@@ -91,6 +93,12 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
|
|||||||
/** 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum millis to wait for a vibration thread cancellation to "clean up" and finish, when
|
||||||
|
* blocking for an external vibration. In practice, this should be plenty.
|
||||||
|
*/
|
||||||
|
private static final long VIBRATION_CANCEL_WAIT_MILLIS = 5000;
|
||||||
|
|
||||||
/** Lifecycle responsible for initializing this class at the right system server phases. */
|
/** Lifecycle responsible for initializing this class at the right system server phases. */
|
||||||
public static class Lifecycle extends SystemService {
|
public static class Lifecycle extends SystemService {
|
||||||
private VibratorManagerService mService;
|
private VibratorManagerService mService;
|
||||||
@@ -121,6 +129,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
|
|||||||
private final PowerManager.WakeLock mWakeLock;
|
private final PowerManager.WakeLock mWakeLock;
|
||||||
private final IBatteryStats mBatteryStatsService;
|
private final IBatteryStats mBatteryStatsService;
|
||||||
private final Handler mHandler;
|
private final Handler mHandler;
|
||||||
|
private final VibrationThread mVibrationThread;
|
||||||
private final AppOpsManager mAppOps;
|
private final AppOpsManager mAppOps;
|
||||||
private final NativeWrapper mNativeWrapper;
|
private final NativeWrapper mNativeWrapper;
|
||||||
private final VibratorManagerRecords mVibratorManagerRecords;
|
private final VibratorManagerRecords mVibratorManagerRecords;
|
||||||
@@ -132,9 +141,9 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
|
|||||||
@GuardedBy("mLock")
|
@GuardedBy("mLock")
|
||||||
private final SparseArray<AlwaysOnVibration> mAlwaysOnEffects = new SparseArray<>();
|
private final SparseArray<AlwaysOnVibration> mAlwaysOnEffects = new SparseArray<>();
|
||||||
@GuardedBy("mLock")
|
@GuardedBy("mLock")
|
||||||
private VibrationThread mCurrentVibration;
|
private VibrationStepConductor mCurrentVibration;
|
||||||
@GuardedBy("mLock")
|
@GuardedBy("mLock")
|
||||||
private VibrationThread mNextVibration;
|
private VibrationStepConductor mNextVibration;
|
||||||
@GuardedBy("mLock")
|
@GuardedBy("mLock")
|
||||||
private ExternalVibrationHolder mCurrentExternalVibration;
|
private ExternalVibrationHolder mCurrentExternalVibration;
|
||||||
@GuardedBy("mLock")
|
@GuardedBy("mLock")
|
||||||
@@ -156,7 +165,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
|
|||||||
clearNextVibrationLocked(Vibration.Status.CANCELLED);
|
clearNextVibrationLocked(Vibration.Status.CANCELLED);
|
||||||
}
|
}
|
||||||
if (shouldCancelOnScreenOffLocked(mCurrentVibration)) {
|
if (shouldCancelOnScreenOffLocked(mCurrentVibration)) {
|
||||||
mCurrentVibration.cancel();
|
mCurrentVibration.notifyCancelled(/* immediate= */ false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -202,6 +211,8 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
|
|||||||
PowerManager pm = context.getSystemService(PowerManager.class);
|
PowerManager pm = context.getSystemService(PowerManager.class);
|
||||||
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*vibrator*");
|
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*vibrator*");
|
||||||
mWakeLock.setReferenceCounted(true);
|
mWakeLock.setReferenceCounted(true);
|
||||||
|
mVibrationThread = new VibrationThread(mWakeLock, mVibrationThreadCallbacks);
|
||||||
|
mVibrationThread.start();
|
||||||
|
|
||||||
// Load vibrator hardware info. The vibrator ids and manager capabilities are loaded only
|
// Load vibrator hardware info. The vibrator ids and manager capabilities are loaded only
|
||||||
// once and assumed unchanged for the lifecycle of this service. Each individual vibrator
|
// once and assumed unchanged for the lifecycle of this service. Each individual vibrator
|
||||||
@@ -409,7 +420,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
|
|||||||
final long ident = Binder.clearCallingIdentity();
|
final long ident = Binder.clearCallingIdentity();
|
||||||
try {
|
try {
|
||||||
if (mCurrentVibration != null) {
|
if (mCurrentVibration != null) {
|
||||||
mCurrentVibration.cancel();
|
mCurrentVibration.notifyCancelled(/* immediate= */ false);
|
||||||
}
|
}
|
||||||
Vibration.Status status = startVibrationLocked(vib);
|
Vibration.Status status = startVibrationLocked(vib);
|
||||||
if (status != Vibration.Status.RUNNING) {
|
if (status != Vibration.Status.RUNNING) {
|
||||||
@@ -447,7 +458,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
|
|||||||
if (mCurrentVibration != null
|
if (mCurrentVibration != null
|
||||||
&& shouldCancelVibration(mCurrentVibration.getVibration(),
|
&& shouldCancelVibration(mCurrentVibration.getVibration(),
|
||||||
usageFilter, token)) {
|
usageFilter, token)) {
|
||||||
mCurrentVibration.cancel();
|
mCurrentVibration.notifyCancelled(/* immediate= */false);
|
||||||
}
|
}
|
||||||
if (mCurrentExternalVibration != null
|
if (mCurrentExternalVibration != null
|
||||||
&& shouldCancelVibration(
|
&& shouldCancelVibration(
|
||||||
@@ -584,7 +595,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
|
|||||||
Slog.d(TAG, "Canceling vibration because settings changed: "
|
Slog.d(TAG, "Canceling vibration because settings changed: "
|
||||||
+ (inputDevicesChanged ? "input devices changed" : ignoreStatus));
|
+ (inputDevicesChanged ? "input devices changed" : ignoreStatus));
|
||||||
}
|
}
|
||||||
mCurrentVibration.cancel();
|
mCurrentVibration.notifyCancelled(/* immediate= */ false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -626,17 +637,15 @@ 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, mVibrationSettings,
|
VibrationStepConductor conductor = new VibrationStepConductor(vib, mVibrationSettings,
|
||||||
mDeviceVibrationEffectAdapter, mVibrators, mWakeLock,
|
mDeviceVibrationEffectAdapter, mVibrators, mVibrationThreadCallbacks);
|
||||||
mVibrationThreadCallbacks);
|
|
||||||
|
|
||||||
if (mCurrentVibration == null) {
|
if (mCurrentVibration == null) {
|
||||||
return startVibrationThreadLocked(vibThread);
|
return startVibrationOnThreadLocked(conductor);
|
||||||
}
|
}
|
||||||
// If there's already a vibration queued (waiting for the previous one to finish
|
// If there's already a vibration queued (waiting for the previous one to finish
|
||||||
// cancelling), end it cleanly and replace it with the new one.
|
// cancelling), end it cleanly and replace it with the new one.
|
||||||
clearNextVibrationLocked(Vibration.Status.IGNORED_SUPERSEDED);
|
clearNextVibrationLocked(Vibration.Status.IGNORED_SUPERSEDED);
|
||||||
mNextVibration = vibThread;
|
mNextVibration = conductor;
|
||||||
return Vibration.Status.RUNNING;
|
return Vibration.Status.RUNNING;
|
||||||
} finally {
|
} finally {
|
||||||
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
|
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
|
||||||
@@ -644,16 +653,20 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@GuardedBy("mLock")
|
@GuardedBy("mLock")
|
||||||
private Vibration.Status startVibrationThreadLocked(VibrationThread vibThread) {
|
private Vibration.Status startVibrationOnThreadLocked(VibrationStepConductor conductor) {
|
||||||
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "startVibrationThreadLocked");
|
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "startVibrationThreadLocked");
|
||||||
try {
|
try {
|
||||||
Vibration vib = vibThread.getVibration();
|
Vibration vib = conductor.getVibration();
|
||||||
int mode = startAppOpModeLocked(vib.uid, vib.opPkg, vib.attrs);
|
int mode = startAppOpModeLocked(vib.uid, vib.opPkg, vib.attrs);
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case AppOpsManager.MODE_ALLOWED:
|
case AppOpsManager.MODE_ALLOWED:
|
||||||
Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
|
Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
|
||||||
mCurrentVibration = vibThread;
|
mCurrentVibration = conductor;
|
||||||
mCurrentVibration.start();
|
if (!mVibrationThread.runVibrationOnVibrationThread(mCurrentVibration)) {
|
||||||
|
// Shouldn't happen. The method call already logs a wtf.
|
||||||
|
mCurrentVibration = null; // Aborted.
|
||||||
|
return Vibration.Status.IGNORED_ERROR_SCHEDULING;
|
||||||
|
}
|
||||||
return Vibration.Status.RUNNING;
|
return Vibration.Status.RUNNING;
|
||||||
case AppOpsManager.MODE_ERRORED:
|
case AppOpsManager.MODE_ERRORED:
|
||||||
Slog.w(TAG, "Start AppOpsManager operation errored for uid " + vib.uid);
|
Slog.w(TAG, "Start AppOpsManager operation errored for uid " + vib.uid);
|
||||||
@@ -741,7 +754,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
|
|||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Slog.d(TAG, "Synced vibration " + vibrationId + " complete, notifying thread");
|
Slog.d(TAG, "Synced vibration " + vibrationId + " complete, notifying thread");
|
||||||
}
|
}
|
||||||
mCurrentVibration.syncedVibrationComplete();
|
mCurrentVibration.notifySyncedVibrationComplete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -753,7 +766,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
|
|||||||
Slog.d(TAG, "Vibration " + vibrationId + " on vibrator " + vibratorId
|
Slog.d(TAG, "Vibration " + vibrationId + " on vibrator " + vibratorId
|
||||||
+ " complete, notifying thread");
|
+ " complete, notifying thread");
|
||||||
}
|
}
|
||||||
mCurrentVibration.vibratorComplete(vibratorId);
|
mCurrentVibration.notifyVibratorComplete(vibratorId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1064,11 +1077,11 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@GuardedBy("mLock")
|
@GuardedBy("mLock")
|
||||||
private boolean shouldCancelOnScreenOffLocked(@Nullable VibrationThread vibrationThread) {
|
private boolean shouldCancelOnScreenOffLocked(@Nullable VibrationStepConductor conductor) {
|
||||||
if (vibrationThread == null) {
|
if (conductor == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Vibration vib = vibrationThread.getVibration();
|
Vibration vib = conductor.getVibration();
|
||||||
return mVibrationSettings.shouldCancelVibrationOnScreenOff(
|
return mVibrationSettings.shouldCancelVibrationOnScreenOff(
|
||||||
vib.uid, vib.opPkg, vib.attrs.getUsage());
|
vib.uid, vib.opPkg, vib.attrs.getUsage());
|
||||||
}
|
}
|
||||||
@@ -1185,21 +1198,27 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onVibrationThreadReleased() {
|
public void onVibrationThreadReleased(long vibrationId) {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Slog.d(TAG, "Vibrators released after finished vibration");
|
Slog.d(TAG, "VibrationThread released after finished vibration");
|
||||||
}
|
}
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Slog.d(TAG, "Processing vibrators released callback");
|
Slog.d(TAG, "Processing VibrationThread released callback");
|
||||||
|
}
|
||||||
|
if (Build.IS_DEBUGGABLE && mCurrentVibration != null
|
||||||
|
&& mCurrentVibration.getVibration().id != vibrationId) {
|
||||||
|
Slog.wtf(TAG, TextUtils.formatSimple(
|
||||||
|
"VibrationId mismatch on release. expected=%d, released=%d",
|
||||||
|
mCurrentVibration.getVibration().id, vibrationId));
|
||||||
}
|
}
|
||||||
mCurrentVibration = null;
|
mCurrentVibration = null;
|
||||||
if (mNextVibration != null) {
|
if (mNextVibration != null) {
|
||||||
VibrationThread vibThread = mNextVibration;
|
VibrationStepConductor nextConductor = mNextVibration;
|
||||||
mNextVibration = null;
|
mNextVibration = null;
|
||||||
Vibration.Status status = startVibrationThreadLocked(vibThread);
|
Vibration.Status status = startVibrationOnThreadLocked(nextConductor);
|
||||||
if (status != Vibration.Status.RUNNING) {
|
if (status != Vibration.Status.RUNNING) {
|
||||||
endVibrationLocked(vibThread.getVibration(), status);
|
endVibrationLocked(nextConductor.getVibration(), status);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1451,7 +1470,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ExternalVibrationHolder cancelingExternalVibration = null;
|
ExternalVibrationHolder cancelingExternalVibration = null;
|
||||||
VibrationThread cancelingVibration = null;
|
boolean waitForCompletion = false;
|
||||||
int scale;
|
int scale;
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
Vibration.Status ignoreStatus = shouldIgnoreVibrationLocked(
|
Vibration.Status ignoreStatus = shouldIgnoreVibrationLocked(
|
||||||
@@ -1473,8 +1492,8 @@ 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) {
|
||||||
clearNextVibrationLocked(Vibration.Status.IGNORED_FOR_EXTERNAL);
|
clearNextVibrationLocked(Vibration.Status.IGNORED_FOR_EXTERNAL);
|
||||||
mCurrentVibration.cancelImmediately();
|
mCurrentVibration.notifyCancelled(/* immediate= */ true);
|
||||||
cancelingVibration = mCurrentVibration;
|
waitForCompletion = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// At this point we have an externally controlled vibration playing already.
|
// At this point we have an externally controlled vibration playing already.
|
||||||
@@ -1497,12 +1516,13 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
|
|||||||
scale = mCurrentExternalVibration.scale;
|
scale = mCurrentExternalVibration.scale;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cancelingVibration != null) {
|
if (waitForCompletion) {
|
||||||
try {
|
if (!mVibrationThread.waitForThreadIdle(VIBRATION_CANCEL_WAIT_MILLIS)) {
|
||||||
cancelingVibration.join();
|
Slog.e(TAG, "Timed out waiting for vibration to cancel");
|
||||||
} catch (InterruptedException e) {
|
synchronized (mLock) {
|
||||||
Slog.w("Interrupted while waiting for vibration to finish before starting "
|
stopExternalVibrateLocked(Vibration.Status.IGNORED_ERROR_CANCELLING);
|
||||||
+ "external control", e);
|
}
|
||||||
|
return IExternalVibratorService.SCALE_MUTE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (cancelingExternalVibration == null) {
|
if (cancelingExternalVibration == null) {
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ import java.util.Arrays;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides {@link VibratorController} with controlled vibrator hardware capabilities and
|
* Provides {@link VibratorController} with controlled vibrator hardware capabilities and
|
||||||
@@ -43,8 +44,8 @@ final class FakeVibratorControllerProvider {
|
|||||||
private static final int EFFECT_DURATION = 20;
|
private static final int EFFECT_DURATION = 20;
|
||||||
|
|
||||||
private final Map<Long, PrebakedSegment> mEnabledAlwaysOnEffects = new HashMap<>();
|
private final Map<Long, PrebakedSegment> mEnabledAlwaysOnEffects = new HashMap<>();
|
||||||
private final List<VibrationEffectSegment> mEffectSegments = new ArrayList<>();
|
private final Map<Long, List<VibrationEffectSegment>> mEffectSegments = new TreeMap<>();
|
||||||
private final List<Integer> mBraking = new ArrayList<>();
|
private final Map<Long, List<Integer>> mBraking = new HashMap<>();
|
||||||
private final List<Float> mAmplitudes = new ArrayList<>();
|
private final List<Float> mAmplitudes = new ArrayList<>();
|
||||||
private final List<Boolean> mExternalControlStates = new ArrayList<>();
|
private final List<Boolean> mExternalControlStates = new ArrayList<>();
|
||||||
private final Handler mHandler;
|
private final Handler mHandler;
|
||||||
@@ -67,6 +68,14 @@ final class FakeVibratorControllerProvider {
|
|||||||
private float mQFactor = Float.NaN;
|
private float mQFactor = Float.NaN;
|
||||||
private float[] mMaxAmplitudes;
|
private float[] mMaxAmplitudes;
|
||||||
|
|
||||||
|
void recordEffectSegment(long vibrationId, VibrationEffectSegment segment) {
|
||||||
|
mEffectSegments.computeIfAbsent(vibrationId, k -> new ArrayList<>()).add(segment);
|
||||||
|
}
|
||||||
|
|
||||||
|
void recordBraking(long vibrationId, int braking) {
|
||||||
|
mBraking.computeIfAbsent(vibrationId, k -> new ArrayList<>()).add(braking);
|
||||||
|
}
|
||||||
|
|
||||||
private final class FakeNativeWrapper extends VibratorController.NativeWrapper {
|
private final class FakeNativeWrapper extends VibratorController.NativeWrapper {
|
||||||
public int vibratorId;
|
public int vibratorId;
|
||||||
public OnVibrationCompleteListener listener;
|
public OnVibrationCompleteListener listener;
|
||||||
@@ -86,7 +95,7 @@ final class FakeVibratorControllerProvider {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long on(long milliseconds, long vibrationId) {
|
public long on(long milliseconds, long vibrationId) {
|
||||||
mEffectSegments.add(new StepSegment(VibrationEffect.DEFAULT_AMPLITUDE,
|
recordEffectSegment(vibrationId, new StepSegment(VibrationEffect.DEFAULT_AMPLITUDE,
|
||||||
/* frequencyHz= */ 0, (int) milliseconds));
|
/* frequencyHz= */ 0, (int) milliseconds));
|
||||||
applyLatency();
|
applyLatency();
|
||||||
scheduleListener(milliseconds, vibrationId);
|
scheduleListener(milliseconds, vibrationId);
|
||||||
@@ -110,7 +119,8 @@ final class FakeVibratorControllerProvider {
|
|||||||
|| Arrays.binarySearch(mSupportedEffects, (int) effect) < 0) {
|
|| Arrays.binarySearch(mSupportedEffects, (int) effect) < 0) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
mEffectSegments.add(new PrebakedSegment((int) effect, false, (int) strength));
|
recordEffectSegment(vibrationId,
|
||||||
|
new PrebakedSegment((int) effect, false, (int) strength));
|
||||||
applyLatency();
|
applyLatency();
|
||||||
scheduleListener(EFFECT_DURATION, vibrationId);
|
scheduleListener(EFFECT_DURATION, vibrationId);
|
||||||
return EFFECT_DURATION;
|
return EFFECT_DURATION;
|
||||||
@@ -121,7 +131,7 @@ final class FakeVibratorControllerProvider {
|
|||||||
long duration = 0;
|
long duration = 0;
|
||||||
for (PrimitiveSegment primitive : effects) {
|
for (PrimitiveSegment primitive : effects) {
|
||||||
duration += EFFECT_DURATION + primitive.getDelay();
|
duration += EFFECT_DURATION + primitive.getDelay();
|
||||||
mEffectSegments.add(primitive);
|
recordEffectSegment(vibrationId, primitive);
|
||||||
}
|
}
|
||||||
applyLatency();
|
applyLatency();
|
||||||
scheduleListener(duration, vibrationId);
|
scheduleListener(duration, vibrationId);
|
||||||
@@ -133,9 +143,9 @@ final class FakeVibratorControllerProvider {
|
|||||||
long duration = 0;
|
long duration = 0;
|
||||||
for (RampSegment primitive : primitives) {
|
for (RampSegment primitive : primitives) {
|
||||||
duration += primitive.getDuration();
|
duration += primitive.getDuration();
|
||||||
mEffectSegments.add(primitive);
|
recordEffectSegment(vibrationId, primitive);
|
||||||
}
|
}
|
||||||
mBraking.add(braking);
|
recordBraking(vibrationId, braking);
|
||||||
applyLatency();
|
applyLatency();
|
||||||
scheduleListener(duration, vibrationId);
|
scheduleListener(duration, vibrationId);
|
||||||
return duration;
|
return duration;
|
||||||
@@ -304,15 +314,35 @@ final class FakeVibratorControllerProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Return the braking values passed to the compose PWLE method. */
|
/** Return the braking values passed to the compose PWLE method. */
|
||||||
public List<Integer> getBraking() {
|
public List<Integer> getBraking(long vibrationId) {
|
||||||
return mBraking;
|
if (mBraking.containsKey(vibrationId)) {
|
||||||
|
return new ArrayList<>(mBraking.get(vibrationId));
|
||||||
|
} else {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Return list of {@link VibrationEffectSegment} played by this controller, in order. */
|
/** Return list of {@link VibrationEffectSegment} played by this controller, in order. */
|
||||||
public List<VibrationEffectSegment> getEffectSegments() {
|
public List<VibrationEffectSegment> getEffectSegments(long vibrationId) {
|
||||||
return new ArrayList<>(mEffectSegments);
|
if (mEffectSegments.containsKey(vibrationId)) {
|
||||||
|
return new ArrayList<>(mEffectSegments.get(vibrationId));
|
||||||
|
} else {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of all vibrations' effect segments, for external-use where vibration IDs
|
||||||
|
* aren't exposed.
|
||||||
|
*/
|
||||||
|
public List<VibrationEffectSegment> getAllEffectSegments() {
|
||||||
|
// Returns segments in order of vibrationId, which increases over time. TreeMap gives order.
|
||||||
|
ArrayList<VibrationEffectSegment> result = new ArrayList<>();
|
||||||
|
for (List<VibrationEffectSegment> subList : mEffectSegments.values()) {
|
||||||
|
result.addAll(subList);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
/** Return list of states set for external control to the fake vibrator hardware. */
|
/** Return list of states set for external control to the fake vibrator hardware. */
|
||||||
public List<Boolean> getExternalControlStates() {
|
public List<Boolean> getExternalControlStates() {
|
||||||
return mExternalControlStates;
|
return mExternalControlStates;
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -571,27 +571,27 @@ public class VibratorManagerServiceTest {
|
|||||||
VibratorManagerService service = createSystemReadyService();
|
VibratorManagerService service = createSystemReadyService();
|
||||||
vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), 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.getAllEffectSegments().isEmpty(),
|
||||||
service, /* timeout= */ 50));
|
service, /* timeout= */ 50));
|
||||||
|
|
||||||
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
|
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
|
||||||
setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 1);
|
setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 1);
|
||||||
service = createSystemReadyService();
|
service = createSystemReadyService();
|
||||||
vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK), RINGTONE_ATTRS);
|
vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK), RINGTONE_ATTRS);
|
||||||
assertTrue(waitUntil(s -> fakeVibrator.getEffectSegments().size() == 1,
|
assertTrue(waitUntil(s -> fakeVibrator.getAllEffectSegments().size() == 1,
|
||||||
service, TEST_TIMEOUT_MILLIS));
|
service, TEST_TIMEOUT_MILLIS));
|
||||||
|
|
||||||
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1);
|
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1);
|
||||||
setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0);
|
setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0);
|
||||||
service = createSystemReadyService();
|
service = createSystemReadyService();
|
||||||
vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK), RINGTONE_ATTRS);
|
vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK), RINGTONE_ATTRS);
|
||||||
assertTrue(waitUntil(s -> fakeVibrator.getEffectSegments().size() == 2,
|
assertTrue(waitUntil(s -> fakeVibrator.getAllEffectSegments().size() == 2,
|
||||||
service, TEST_TIMEOUT_MILLIS));
|
service, TEST_TIMEOUT_MILLIS));
|
||||||
|
|
||||||
assertEquals(
|
assertEquals(
|
||||||
Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_HEAVY_CLICK),
|
Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_HEAVY_CLICK),
|
||||||
expectedPrebaked(VibrationEffect.EFFECT_DOUBLE_CLICK)),
|
expectedPrebaked(VibrationEffect.EFFECT_DOUBLE_CLICK)),
|
||||||
mVibratorProviders.get(1).getEffectSegments());
|
mVibratorProviders.get(1).getAllEffectSegments());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -604,25 +604,25 @@ public class VibratorManagerServiceTest {
|
|||||||
mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
|
mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
|
||||||
vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_TICK), HAPTIC_FEEDBACK_ATTRS);
|
vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_TICK), HAPTIC_FEEDBACK_ATTRS);
|
||||||
vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), RINGTONE_ATTRS);
|
vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), RINGTONE_ATTRS);
|
||||||
assertTrue(waitUntil(s -> fakeVibrator.getEffectSegments().size() == 1,
|
assertTrue(waitUntil(s -> fakeVibrator.getAllEffectSegments().size() == 1,
|
||||||
service, TEST_TIMEOUT_MILLIS));
|
service, TEST_TIMEOUT_MILLIS));
|
||||||
|
|
||||||
mRegisteredPowerModeListener.onLowPowerModeChanged(NORMAL_POWER_STATE);
|
mRegisteredPowerModeListener.onLowPowerModeChanged(NORMAL_POWER_STATE);
|
||||||
vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK),
|
vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK),
|
||||||
/* attrs= */ null);
|
/* attrs= */ null);
|
||||||
assertTrue(waitUntil(s -> fakeVibrator.getEffectSegments().size() == 2,
|
assertTrue(waitUntil(s -> fakeVibrator.getAllEffectSegments().size() == 2,
|
||||||
service, TEST_TIMEOUT_MILLIS));
|
service, TEST_TIMEOUT_MILLIS));
|
||||||
|
|
||||||
vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK),
|
vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK),
|
||||||
NOTIFICATION_ATTRS);
|
NOTIFICATION_ATTRS);
|
||||||
assertTrue(waitUntil(s -> fakeVibrator.getEffectSegments().size() == 3,
|
assertTrue(waitUntil(s -> fakeVibrator.getAllEffectSegments().size() == 3,
|
||||||
service, TEST_TIMEOUT_MILLIS));
|
service, TEST_TIMEOUT_MILLIS));
|
||||||
|
|
||||||
assertEquals(
|
assertEquals(
|
||||||
Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_CLICK),
|
Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_CLICK),
|
||||||
expectedPrebaked(VibrationEffect.EFFECT_HEAVY_CLICK),
|
expectedPrebaked(VibrationEffect.EFFECT_HEAVY_CLICK),
|
||||||
expectedPrebaked(VibrationEffect.EFFECT_DOUBLE_CLICK)),
|
expectedPrebaked(VibrationEffect.EFFECT_DOUBLE_CLICK)),
|
||||||
mVibratorProviders.get(1).getEffectSegments());
|
mVibratorProviders.get(1).getAllEffectSegments());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -738,14 +738,14 @@ public class VibratorManagerServiceTest {
|
|||||||
VibrationAttributes.USAGE_UNKNOWN).build());
|
VibrationAttributes.USAGE_UNKNOWN).build());
|
||||||
|
|
||||||
// VibrationThread will start this vibration async, so wait before checking it started.
|
// VibrationThread will start this vibration async, so wait before checking it started.
|
||||||
assertTrue(waitUntil(s -> !mVibratorProviders.get(1).getEffectSegments().isEmpty(),
|
assertTrue(waitUntil(s -> !mVibratorProviders.get(1).getAllEffectSegments().isEmpty(),
|
||||||
service, TEST_TIMEOUT_MILLIS));
|
service, TEST_TIMEOUT_MILLIS));
|
||||||
|
|
||||||
vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK),
|
vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK),
|
||||||
HAPTIC_FEEDBACK_ATTRS);
|
HAPTIC_FEEDBACK_ATTRS);
|
||||||
|
|
||||||
// Wait before checking it never played a second effect.
|
// Wait before checking it never played a second effect.
|
||||||
assertFalse(waitUntil(s -> mVibratorProviders.get(1).getEffectSegments().size() > 1,
|
assertFalse(waitUntil(s -> mVibratorProviders.get(1).getAllEffectSegments().size() > 1,
|
||||||
service, /* timeout= */ 50));
|
service, /* timeout= */ 50));
|
||||||
|
|
||||||
// The time estimate is recorded when the vibration starts, repeating vibrations
|
// The time estimate is recorded when the vibration starts, repeating vibrations
|
||||||
@@ -767,14 +767,14 @@ public class VibratorManagerServiceTest {
|
|||||||
VibrationAttributes.USAGE_ALARM).build());
|
VibrationAttributes.USAGE_ALARM).build());
|
||||||
|
|
||||||
// VibrationThread will start this vibration async, so wait before checking it started.
|
// VibrationThread will start this vibration async, so wait before checking it started.
|
||||||
assertTrue(waitUntil(s -> !mVibratorProviders.get(1).getEffectSegments().isEmpty(),
|
assertTrue(waitUntil(s -> !mVibratorProviders.get(1).getAllEffectSegments().isEmpty(),
|
||||||
service, TEST_TIMEOUT_MILLIS));
|
service, TEST_TIMEOUT_MILLIS));
|
||||||
|
|
||||||
vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK),
|
vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK),
|
||||||
HAPTIC_FEEDBACK_ATTRS);
|
HAPTIC_FEEDBACK_ATTRS);
|
||||||
|
|
||||||
// Wait before checking it never played a second effect.
|
// Wait before checking it never played a second effect.
|
||||||
assertFalse(waitUntil(s -> mVibratorProviders.get(1).getEffectSegments().size() > 1,
|
assertFalse(waitUntil(s -> mVibratorProviders.get(1).getAllEffectSegments().size() > 1,
|
||||||
service, /* timeout= */ 50));
|
service, /* timeout= */ 50));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -798,7 +798,7 @@ public class VibratorManagerServiceTest {
|
|||||||
verify(mIInputManagerMock).vibrateCombined(eq(1), eq(effect), any());
|
verify(mIInputManagerMock).vibrateCombined(eq(1), eq(effect), any());
|
||||||
|
|
||||||
// VibrationThread will start this vibration async, so wait before checking it never played.
|
// VibrationThread will start this vibration async, so wait before checking it never played.
|
||||||
assertFalse(waitUntil(s -> !mVibratorProviders.get(1).getEffectSegments().isEmpty(),
|
assertFalse(waitUntil(s -> !mVibratorProviders.get(1).getAllEffectSegments().isEmpty(),
|
||||||
service, /* timeout= */ 50));
|
service, /* timeout= */ 50));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -863,8 +863,8 @@ public class VibratorManagerServiceTest {
|
|||||||
verify(mNativeWrapperMock).triggerSynced(anyLong());
|
verify(mNativeWrapperMock).triggerSynced(anyLong());
|
||||||
PrimitiveSegment expected = new PrimitiveSegment(
|
PrimitiveSegment expected = new PrimitiveSegment(
|
||||||
VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 100);
|
VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 100);
|
||||||
assertEquals(Arrays.asList(expected), mVibratorProviders.get(1).getEffectSegments());
|
assertEquals(Arrays.asList(expected), mVibratorProviders.get(1).getAllEffectSegments());
|
||||||
assertEquals(Arrays.asList(expected), mVibratorProviders.get(2).getEffectSegments());
|
assertEquals(Arrays.asList(expected), mVibratorProviders.get(2).getAllEffectSegments());
|
||||||
|
|
||||||
// VibrationThread needs some time to react to native callbacks and stop the vibrator.
|
// VibrationThread needs some time to react to native callbacks and stop the vibrator.
|
||||||
assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
|
assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
|
||||||
@@ -891,7 +891,7 @@ public class VibratorManagerServiceTest {
|
|||||||
.compose())
|
.compose())
|
||||||
.combine();
|
.combine();
|
||||||
vibrate(service, effect, ALARM_ATTRS);
|
vibrate(service, effect, ALARM_ATTRS);
|
||||||
assertTrue(waitUntil(s -> !fakeVibrator1.getEffectSegments().isEmpty(), service,
|
assertTrue(waitUntil(s -> !fakeVibrator1.getAllEffectSegments().isEmpty(), service,
|
||||||
TEST_TIMEOUT_MILLIS));
|
TEST_TIMEOUT_MILLIS));
|
||||||
|
|
||||||
verify(mNativeWrapperMock).prepareSynced(eq(new int[]{1, 2}));
|
verify(mNativeWrapperMock).prepareSynced(eq(new int[]{1, 2}));
|
||||||
@@ -915,7 +915,7 @@ public class VibratorManagerServiceTest {
|
|||||||
.addVibrator(2, VibrationEffect.createOneShot(10, 100))
|
.addVibrator(2, VibrationEffect.createOneShot(10, 100))
|
||||||
.combine();
|
.combine();
|
||||||
vibrate(service, effect, ALARM_ATTRS);
|
vibrate(service, effect, ALARM_ATTRS);
|
||||||
assertTrue(waitUntil(s -> !fakeVibrator1.getEffectSegments().isEmpty(), service,
|
assertTrue(waitUntil(s -> !fakeVibrator1.getAllEffectSegments().isEmpty(), service,
|
||||||
TEST_TIMEOUT_MILLIS));
|
TEST_TIMEOUT_MILLIS));
|
||||||
|
|
||||||
verify(mNativeWrapperMock, never()).prepareSynced(any());
|
verify(mNativeWrapperMock, never()).prepareSynced(any());
|
||||||
@@ -935,8 +935,8 @@ public class VibratorManagerServiceTest {
|
|||||||
.addVibrator(2, VibrationEffect.createOneShot(10, 100))
|
.addVibrator(2, VibrationEffect.createOneShot(10, 100))
|
||||||
.combine();
|
.combine();
|
||||||
vibrate(service, effect, ALARM_ATTRS);
|
vibrate(service, effect, ALARM_ATTRS);
|
||||||
assertTrue(waitUntil(s -> !mVibratorProviders.get(1).getEffectSegments().isEmpty(), service,
|
assertTrue(waitUntil(s -> !mVibratorProviders.get(1).getAllEffectSegments().isEmpty(),
|
||||||
TEST_TIMEOUT_MILLIS));
|
service, TEST_TIMEOUT_MILLIS));
|
||||||
|
|
||||||
verify(mNativeWrapperMock).prepareSynced(eq(new int[]{1, 2}));
|
verify(mNativeWrapperMock).prepareSynced(eq(new int[]{1, 2}));
|
||||||
verify(mNativeWrapperMock, never()).triggerSynced(anyLong());
|
verify(mNativeWrapperMock, never()).triggerSynced(anyLong());
|
||||||
@@ -956,8 +956,8 @@ public class VibratorManagerServiceTest {
|
|||||||
.addVibrator(2, VibrationEffect.createOneShot(10, 100))
|
.addVibrator(2, VibrationEffect.createOneShot(10, 100))
|
||||||
.combine();
|
.combine();
|
||||||
vibrate(service, effect, ALARM_ATTRS);
|
vibrate(service, effect, ALARM_ATTRS);
|
||||||
assertTrue(waitUntil(s -> !mVibratorProviders.get(1).getEffectSegments().isEmpty(), service,
|
assertTrue(waitUntil(s -> !mVibratorProviders.get(1).getAllEffectSegments().isEmpty(),
|
||||||
TEST_TIMEOUT_MILLIS));
|
service, TEST_TIMEOUT_MILLIS));
|
||||||
|
|
||||||
verify(mNativeWrapperMock).prepareSynced(eq(new int[]{1, 2}));
|
verify(mNativeWrapperMock).prepareSynced(eq(new int[]{1, 2}));
|
||||||
verify(mNativeWrapperMock).triggerSynced(anyLong());
|
verify(mNativeWrapperMock).triggerSynced(anyLong());
|
||||||
@@ -995,26 +995,26 @@ public class VibratorManagerServiceTest {
|
|||||||
vibrate(service, CombinedVibration.startSequential()
|
vibrate(service, CombinedVibration.startSequential()
|
||||||
.addNext(1, VibrationEffect.createOneShot(100, 125))
|
.addNext(1, VibrationEffect.createOneShot(100, 125))
|
||||||
.combine(), NOTIFICATION_ATTRS);
|
.combine(), NOTIFICATION_ATTRS);
|
||||||
assertTrue(waitUntil(s -> fakeVibrator.getEffectSegments().size() == 1,
|
assertTrue(waitUntil(s -> fakeVibrator.getAllEffectSegments().size() == 1,
|
||||||
service, TEST_TIMEOUT_MILLIS));
|
service, TEST_TIMEOUT_MILLIS));
|
||||||
|
|
||||||
vibrate(service, VibrationEffect.startComposition()
|
vibrate(service, VibrationEffect.startComposition()
|
||||||
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f)
|
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f)
|
||||||
.compose(), HAPTIC_FEEDBACK_ATTRS);
|
.compose(), HAPTIC_FEEDBACK_ATTRS);
|
||||||
assertTrue(waitUntil(s -> fakeVibrator.getEffectSegments().size() == 2,
|
assertTrue(waitUntil(s -> fakeVibrator.getAllEffectSegments().size() == 2,
|
||||||
service, TEST_TIMEOUT_MILLIS));
|
service, TEST_TIMEOUT_MILLIS));
|
||||||
|
|
||||||
vibrate(service, VibrationEffect.startComposition()
|
vibrate(service, VibrationEffect.startComposition()
|
||||||
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f)
|
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f)
|
||||||
.compose(), ALARM_ATTRS);
|
.compose(), ALARM_ATTRS);
|
||||||
assertTrue(waitUntil(s -> fakeVibrator.getEffectSegments().size() == 3,
|
assertTrue(waitUntil(s -> fakeVibrator.getAllEffectSegments().size() == 3,
|
||||||
service, TEST_TIMEOUT_MILLIS));
|
service, TEST_TIMEOUT_MILLIS));
|
||||||
|
|
||||||
vibrate(service, VibrationEffect.createOneShot(100, 125), RINGTONE_ATTRS);
|
vibrate(service, VibrationEffect.createOneShot(100, 125), RINGTONE_ATTRS);
|
||||||
assertFalse(waitUntil(s -> fakeVibrator.getEffectSegments().size() > 3,
|
assertFalse(waitUntil(s -> fakeVibrator.getAllEffectSegments().size() > 3,
|
||||||
service, TEST_TIMEOUT_MILLIS));
|
service, TEST_TIMEOUT_MILLIS));
|
||||||
|
|
||||||
assertEquals(3, fakeVibrator.getEffectSegments().size());
|
assertEquals(3, fakeVibrator.getAllEffectSegments().size());
|
||||||
|
|
||||||
// Notification vibrations will be scaled with SCALE_HIGH or none if default is high.
|
// Notification vibrations will be scaled with SCALE_HIGH or none if default is high.
|
||||||
assertEquals(defaultNotificationIntensity < Vibrator.VIBRATION_INTENSITY_HIGH,
|
assertEquals(defaultNotificationIntensity < Vibrator.VIBRATION_INTENSITY_HIGH,
|
||||||
@@ -1022,11 +1022,11 @@ public class VibratorManagerServiceTest {
|
|||||||
|
|
||||||
// Haptic feedback vibrations will be scaled with SCALE_LOW or none if default is low.
|
// Haptic feedback vibrations will be scaled with SCALE_LOW or none if default is low.
|
||||||
assertEquals(defaultTouchIntensity > Vibrator.VIBRATION_INTENSITY_LOW,
|
assertEquals(defaultTouchIntensity > Vibrator.VIBRATION_INTENSITY_LOW,
|
||||||
0.5 > ((PrimitiveSegment) fakeVibrator.getEffectSegments().get(1)).getScale());
|
0.5 > ((PrimitiveSegment) fakeVibrator.getAllEffectSegments().get(1)).getScale());
|
||||||
|
|
||||||
// Alarm vibration will be scaled with SCALE_NONE.
|
// Alarm vibration will be scaled with SCALE_NONE.
|
||||||
assertEquals(1f,
|
assertEquals(1f,
|
||||||
((PrimitiveSegment) fakeVibrator.getEffectSegments().get(2)).getScale(), 1e-5);
|
((PrimitiveSegment) fakeVibrator.getAllEffectSegments().get(2)).getScale(), 1e-5);
|
||||||
|
|
||||||
// Ring vibrations have intensity OFF and are not played.
|
// Ring vibrations have intensity OFF and are not played.
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user