Deterministic PowerManagerServiceTest
To make PowerManagerServiceTest deterministic,
remove dependency on real time clock by mocking it out.
Use TestLooper, which allows making Handler behaviour deterministic as
well.
Test: atest PowerManagerServiceTest
Bug: 152193749
Change-Id: I4d9cd686ac672261bbb038249c0358e451b58710
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index b1c40cc..5025835 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -241,7 +241,7 @@
private final Context mContext;
private final ServiceThread mHandlerThread;
- private final PowerManagerHandler mHandler;
+ private final Handler mHandler;
private final AmbientDisplayConfiguration mAmbientDisplayConfiguration;
private final BatterySaverController mBatterySaverController;
private final BatterySaverPolicy mBatterySaverPolicy;
@@ -252,6 +252,7 @@
private final LocalService mLocalService;
private final NativeWrapper mNativeWrapper;
private final SystemPropertiesWrapper mSystemProperties;
+ private final Clock mClock;
private final Injector mInjector;
private LightsManager mLightsManager;
@@ -597,7 +598,7 @@
@Override
public void onForegroundProfileSwitch(@UserIdInt int newProfileId) throws RemoteException {
- final long now = SystemClock.uptimeMillis();
+ final long now = mClock.uptimeMillis();
synchronized (mLock) {
mForegroundProfile = newProfileId;
maybeUpdateForegroundProfileLastActivityLocked(now);
@@ -625,11 +626,11 @@
// Whether profile has been locked last time it timed out.
boolean mLockingNotified;
- public ProfilePowerState(@UserIdInt int userId, long screenOffTimeout) {
+ public ProfilePowerState(@UserIdInt int userId, long screenOffTimeout, long now) {
mUserId = userId;
mScreenOffTimeout = screenOffTimeout;
// Not accurate but at least won't cause immediate locking of the profile.
- mLastUserActivityTime = SystemClock.uptimeMillis();
+ mLastUserActivityTime = now;
}
}
@@ -756,6 +757,15 @@
}
}
+ /** Functional interface for providing time. */
+ @VisibleForTesting
+ interface Clock {
+ /**
+ * Returns current time in milliseconds since boot, not counting time spent in deep sleep.
+ */
+ long uptimeMillis();
+ }
+
@VisibleForTesting
static class Injector {
Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats,
@@ -822,6 +832,17 @@
};
}
+ Clock createClock() {
+ return SystemClock::uptimeMillis;
+ }
+
+ /**
+ * Handler for asynchronous operations performed by the power manager.
+ */
+ Handler createHandler(Looper looper, Handler.Callback callback) {
+ return new Handler(looper, callback, true /*async*/);
+ }
+
void invalidateIsInteractiveCaches() {
PowerManager.invalidateIsInteractiveCaches();
}
@@ -853,12 +874,14 @@
mLocalService = new LocalService();
mNativeWrapper = injector.createNativeWrapper();
mSystemProperties = injector.createSystemPropertiesWrapper();
+ mClock = injector.createClock();
mInjector = injector;
mHandlerThread = new ServiceThread(TAG,
Process.THREAD_PRIORITY_DISPLAY, false /*allowIo*/);
mHandlerThread.start();
- mHandler = new PowerManagerHandler(mHandlerThread.getLooper());
+ mHandler = injector.createHandler(mHandlerThread.getLooper(),
+ new PowerManagerHandlerCallback());
mConstants = new Constants(mHandler);
mAmbientDisplayConfiguration = mInjector.createAmbientDisplayConfiguration(context);
mAmbientDisplaySuppressionController =
@@ -1002,7 +1025,7 @@
incrementBootCount();
} else if (phase == PHASE_BOOT_COMPLETED) {
- final long now = SystemClock.uptimeMillis();
+ final long now = mClock.uptimeMillis();
mBootCompleted = true;
mDirty |= DIRTY_BOOT_COMPLETED;
@@ -1011,7 +1034,7 @@
now, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
if (sQuiescent) {
- goToSleepNoUpdateLocked(SystemClock.uptimeMillis(),
+ goToSleepNoUpdateLocked(mClock.uptimeMillis(),
PowerManager.GO_TO_SLEEP_REASON_QUIESCENT,
PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE, Process.SYSTEM_UID);
}
@@ -1354,7 +1377,7 @@
opPackageName = wakeLock.mPackageName;
opUid = wakeLock.mOwnerUid;
}
- wakeUpNoUpdateLocked(SystemClock.uptimeMillis(),
+ wakeUpNoUpdateLocked(mClock.uptimeMillis(),
PowerManager.WAKE_REASON_APPLICATION, wakeLock.mTag,
opUid, opPackageName, opUid);
}
@@ -1420,7 +1443,7 @@
private void applyWakeLockFlagsOnReleaseLocked(WakeLock wakeLock) {
if ((wakeLock.mFlags & PowerManager.ON_AFTER_RELEASE) != 0
&& isScreenLock(wakeLock)) {
- userActivityNoUpdateLocked(SystemClock.uptimeMillis(),
+ userActivityNoUpdateLocked(mClock.uptimeMillis(),
PowerManager.USER_ACTIVITY_EVENT_OTHER,
PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS,
wakeLock.mOwnerUid);
@@ -1484,7 +1507,7 @@
}
private void restartNofifyLongTimerLocked(WakeLock wakeLock) {
- wakeLock.mAcquireTime = SystemClock.uptimeMillis();
+ wakeLock.mAcquireTime = mClock.uptimeMillis();
if ((wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK)
== PowerManager.PARTIAL_WAKE_LOCK && mNotifyLongScheduled == 0) {
enqueueNotifyLongMsgLocked(wakeLock.mAcquireTime + MIN_LONG_WAKE_CHECK_INTERVAL);
@@ -1569,7 +1592,7 @@
private void onUserAttention() {
synchronized (mLock) {
- if (userActivityNoUpdateLocked(SystemClock.uptimeMillis(),
+ if (userActivityNoUpdateLocked(mClock.uptimeMillis(),
PowerManager.USER_ACTIVITY_EVENT_ATTENTION, 0 /* flags */,
Process.SYSTEM_UID)) {
updatePowerStateLocked();
@@ -1844,7 +1867,7 @@
* had the system not been told the user was inactive.
*/
private void logSleepTimeoutRecapturedLocked() {
- final long now = SystemClock.uptimeMillis();
+ final long now = mClock.uptimeMillis();
final long savedWakeTimeMs = mOverriddenTimeout - now;
if (savedWakeTimeMs >= 0) {
EventLogTags.writePowerSoftSleepRequested(savedWakeTimeMs);
@@ -1867,7 +1890,7 @@
}
if (getWakefulnessLocked() == WAKEFULNESS_AWAKE) {
Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, TRACE_SCREEN_ON, 0);
- final int latencyMs = (int) (SystemClock.uptimeMillis() - mLastWakeTime);
+ final int latencyMs = (int) (mClock.uptimeMillis() - mLastWakeTime);
if (latencyMs >= SCREEN_ON_LATENCY_WARNING_MS) {
Slog.w(TAG, "Screen on took " + latencyMs + " ms");
}
@@ -1903,7 +1926,7 @@
// Phase 1: Update wakefulness.
// Loop because the wake lock and user activity computations are influenced
// by changes in wakefulness.
- final long now = SystemClock.uptimeMillis();
+ final long now = mClock.uptimeMillis();
int dirtyPhase2 = 0;
for (;;) {
int dirtyPhase1 = mDirty;
@@ -1996,7 +2019,7 @@
// and it shuts off right away.
// Some devices also wake the device when plugged or unplugged because
// they don't have a charging LED.
- final long now = SystemClock.uptimeMillis();
+ final long now = mClock.uptimeMillis();
if (shouldWakeUpWhenPluggedOrUnpluggedLocked(wasPowered, oldPlugType,
dockedOnWirelessCharger)) {
wakeUpNoUpdateLocked(now, PowerManager.WAKE_REASON_PLUGGED_IN,
@@ -2204,7 +2227,7 @@
void checkForLongWakeLocks() {
synchronized (mLock) {
- final long now = SystemClock.uptimeMillis();
+ final long now = mClock.uptimeMillis();
mNotifyLongDispatched = now;
final long when = now - MIN_LONG_WAKE_CHECK_INTERVAL;
long nextCheckTime = Long.MAX_VALUE;
@@ -2533,7 +2556,7 @@
if (DEBUG_SPEW) {
Slog.d(TAG, "updateWakefulnessLocked: Bed time...");
}
- final long time = SystemClock.uptimeMillis();
+ final long time = mClock.uptimeMillis();
if (isAttentiveTimeoutExpired(time)) {
changed = goToSleepNoUpdateLocked(time, PowerManager.GO_TO_SLEEP_REASON_TIMEOUT,
PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE, Process.SYSTEM_UID);
@@ -2568,7 +2591,7 @@
return false;
}
- long now = SystemClock.uptimeMillis();
+ long now = mClock.uptimeMillis();
if (isAttentiveTimeoutExpired(now)) {
return !isBeingKeptFromInattentiveSleepLocked();
} else {
@@ -2699,7 +2722,7 @@
}
// Determine whether the dream should continue.
- long now = SystemClock.uptimeMillis();
+ long now = mClock.uptimeMillis();
if (wakefulness == WAKEFULNESS_DREAMING) {
if (isDreaming && canDreamLocked()) {
if (mDreamsBatteryLevelDrainCutoffConfig >= 0
@@ -2881,7 +2904,7 @@
private void updateScreenBrightnessBoostLocked(int dirty) {
if ((dirty & DIRTY_SCREEN_BRIGHTNESS_BOOST) != 0) {
if (mScreenBrightnessBoostInProgress) {
- final long now = SystemClock.uptimeMillis();
+ final long now = mClock.uptimeMillis();
mHandler.removeMessages(MSG_SCREEN_BRIGHTNESS_BOOST_TIMEOUT);
if (mLastScreenBrightnessBoostTime > mLastSleepTime) {
final long boostTimeout = mLastScreenBrightnessBoostTime +
@@ -2968,7 +2991,7 @@
synchronized (mLock) {
mProximityPositive = false;
mDirty |= DIRTY_PROXIMITY_POSITIVE;
- userActivityNoUpdateLocked(SystemClock.uptimeMillis(),
+ userActivityNoUpdateLocked(mClock.uptimeMillis(),
PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
updatePowerStateLocked();
}
@@ -3289,7 +3312,8 @@
if (profile != null) {
profile.mScreenOffTimeout = timeMs;
} else {
- mProfilePowerState.put(userId, new ProfilePowerState(userId, timeMs));
+ mProfilePowerState.put(userId, new ProfilePowerState(userId, timeMs,
+ mClock.uptimeMillis()));
// We need to recalculate wake locks for the new profile state.
mDirty |= DIRTY_WAKE_LOCKS;
}
@@ -3646,14 +3670,14 @@
@VisibleForTesting
boolean wasDeviceIdleForInternal(long ms) {
synchronized (mLock) {
- return mLastUserActivityTime + ms < SystemClock.uptimeMillis();
+ return mLastUserActivityTime + ms < mClock.uptimeMillis();
}
}
@VisibleForTesting
void onUserActivity() {
synchronized (mLock) {
- mLastUserActivityTime = SystemClock.uptimeMillis();
+ mLastUserActivityTime = mClock.uptimeMillis();
}
}
@@ -3662,7 +3686,7 @@
synchronized (mLock) {
mForceSuspendActive = true;
// Place the system in an non-interactive state
- goToSleepInternal(SystemClock.uptimeMillis(),
+ goToSleepInternal(mClock.uptimeMillis(),
PowerManager.GO_TO_SLEEP_REASON_FORCE_SUSPEND,
PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE, uid);
@@ -3777,21 +3801,21 @@
if (mNotifyLongScheduled == 0) {
pw.print("(none)");
} else {
- TimeUtils.formatDuration(mNotifyLongScheduled, SystemClock.uptimeMillis(), pw);
+ TimeUtils.formatDuration(mNotifyLongScheduled, mClock.uptimeMillis(), pw);
}
pw.println();
pw.print(" mNotifyLongDispatched=");
if (mNotifyLongDispatched == 0) {
pw.print("(none)");
} else {
- TimeUtils.formatDuration(mNotifyLongDispatched, SystemClock.uptimeMillis(), pw);
+ TimeUtils.formatDuration(mNotifyLongDispatched, mClock.uptimeMillis(), pw);
}
pw.println();
pw.print(" mNotifyLongNextCheck=");
if (mNotifyLongNextCheck == 0) {
pw.print("(none)");
} else {
- TimeUtils.formatDuration(mNotifyLongNextCheck, SystemClock.uptimeMillis(), pw);
+ TimeUtils.formatDuration(mNotifyLongNextCheck, mClock.uptimeMillis(), pw);
}
pw.println();
pw.println(" mUserActivitySummary=0x" + Integer.toHexString(mUserActivitySummary));
@@ -4389,15 +4413,11 @@
};
/**
- * Handler for asynchronous operations performed by the power manager.
+ * Callback for asynchronous operations performed by the power manager.
*/
- private final class PowerManagerHandler extends Handler {
- public PowerManagerHandler(Looper looper) {
- super(looper, null, true /*async*/);
- }
-
+ private final class PowerManagerHandlerCallback implements Handler.Callback {
@Override
- public void handleMessage(Message msg) {
+ public boolean handleMessage(Message msg) {
switch (msg.what) {
case MSG_USER_ACTIVITY_TIMEOUT:
handleUserActivityTimeout();
@@ -4415,6 +4435,8 @@
handleAttentiveTimeout();
break;
}
+
+ return true;
}
}
@@ -4505,7 +4527,7 @@
}
if (mNotifiedAcquired) {
sb.append(" ACQ=");
- TimeUtils.formatDuration(mAcquireTime-SystemClock.uptimeMillis(), sb);
+ TimeUtils.formatDuration(mAcquireTime-mClock.uptimeMillis(), sb);
}
if (mNotifiedLong) {
sb.append(" LONG");
@@ -4817,7 +4839,7 @@
@Override // Binder call
public void userActivity(long eventTime, int event, int flags) {
- final long now = SystemClock.uptimeMillis();
+ final long now = mClock.uptimeMillis();
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER)
!= PackageManager.PERMISSION_GRANTED
&& mContext.checkCallingOrSelfPermission(
@@ -4855,7 +4877,7 @@
@Override // Binder call
public void wakeUp(long eventTime, @WakeReason int reason, String details,
String opPackageName) {
- if (eventTime > SystemClock.uptimeMillis()) {
+ if (eventTime > mClock.uptimeMillis()) {
throw new IllegalArgumentException("event time must not be in the future");
}
@@ -4873,7 +4895,7 @@
@Override // Binder call
public void goToSleep(long eventTime, int reason, int flags) {
- if (eventTime > SystemClock.uptimeMillis()) {
+ if (eventTime > mClock.uptimeMillis()) {
throw new IllegalArgumentException("event time must not be in the future");
}
@@ -4891,7 +4913,7 @@
@Override // Binder call
public void nap(long eventTime) {
- if (eventTime > SystemClock.uptimeMillis()) {
+ if (eventTime > mClock.uptimeMillis()) {
throw new IllegalArgumentException("event time must not be in the future");
}
@@ -5286,7 +5308,7 @@
@Override // Binder call
public void boostScreenBrightness(long eventTime) {
- if (eventTime > SystemClock.uptimeMillis()) {
+ if (eventTime > mClock.uptimeMillis()) {
throw new IllegalArgumentException("event time must not be in the future");
}
diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
index bb51471..d244e68 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -61,8 +61,8 @@
import android.os.Looper;
import android.os.PowerManager;
import android.os.PowerSaveState;
-import android.os.SystemClock;
import android.os.UserHandle;
+import android.os.test.TestLooper;
import android.provider.Settings;
import android.service.dreams.DreamManagerInternal;
import android.test.mock.MockContentResolver;
@@ -84,6 +84,7 @@
import com.android.server.power.batterysaver.BatterySaverPolicy;
import com.android.server.power.batterysaver.BatterySaverStateMachine;
import com.android.server.power.batterysaver.BatterySavingStats;
+import com.android.server.testutils.OffsettableClock;
import org.junit.After;
import org.junit.Before;
@@ -132,6 +133,8 @@
private BatteryReceiver mBatteryReceiver;
private UserSwitchedReceiver mUserSwitchedReceiver;
private Resources mResourcesSpy;
+ private OffsettableClock mClock;
+ private TestLooper mTestLooper;
private class IntentFilterMatcher implements ArgumentMatcher<IntentFilter> {
private final IntentFilter mFilter;
@@ -189,6 +192,9 @@
Settings.Global.putInt(mContextSpy.getContentResolver(),
Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0);
+
+ mClock = new OffsettableClock.Stopped();
+ mTestLooper = new TestLooper(mClock::now);
}
private PowerManagerService createService() {
@@ -250,6 +256,16 @@
}
@Override
+ PowerManagerService.Clock createClock() {
+ return () -> mClock.now();
+ }
+
+ @Override
+ Handler createHandler(Looper looper, Handler.Callback callback) {
+ return new Handler(mTestLooper.getLooper(), callback);
+ }
+
+ @Override
void invalidateIsInteractiveCaches() {
// Avoids an SELinux failure.
}
@@ -297,21 +313,21 @@
}
private void forceSleep() {
- mService.getBinderServiceInstance().goToSleep(SystemClock.uptimeMillis(),
+ mService.getBinderServiceInstance().goToSleep(mClock.now(),
PowerManager.GO_TO_SLEEP_REASON_APPLICATION, PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
}
private void forceDream() {
- mService.getBinderServiceInstance().nap(SystemClock.uptimeMillis());
+ mService.getBinderServiceInstance().nap(mClock.now());
}
private void forceAwake() {
- mService.getBinderServiceInstance().wakeUp(SystemClock.uptimeMillis(),
+ mService.getBinderServiceInstance().wakeUp(mClock.now(),
PowerManager.WAKE_REASON_UNKNOWN, "testing IPowerManager.wakeUp()", "pkg.name");
}
private void forceDozing() {
- mService.getBinderServiceInstance().goToSleep(SystemClock.uptimeMillis(),
+ mService.getBinderServiceInstance().goToSleep(mClock.now(),
PowerManager.GO_TO_SLEEP_REASON_APPLICATION, 0);
}
@@ -341,6 +357,11 @@
.thenReturn(minimumScreenOffTimeoutConfigMillis);
}
+ private void advanceTime(long timeMs) {
+ mClock.fastForward(timeMs);
+ mTestLooper.dispatchAll();
+ }
+
@Test
public void testUpdatePowerScreenPolicy_UpdateDisplayPowerRequest() {
createService();
@@ -403,7 +424,7 @@
assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
// Take a nap and verify.
- mService.getBinderServiceInstance().goToSleep(SystemClock.uptimeMillis(),
+ mService.getBinderServiceInstance().goToSleep(mClock.now(),
PowerManager.GO_TO_SLEEP_REASON_APPLICATION, PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_ASLEEP);
}
@@ -445,7 +466,7 @@
createService();
startSystem();
forceSleep();
- mService.getBinderServiceInstance().wakeUp(SystemClock.uptimeMillis(),
+ mService.getBinderServiceInstance().wakeUp(mClock.now(),
PowerManager.WAKE_REASON_UNKNOWN, "testing IPowerManager.wakeUp()", "pkg.name");
assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
}
@@ -540,7 +561,7 @@
assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
// Take a nap and verify.
- mService.getBinderServiceInstance().goToSleep(SystemClock.uptimeMillis(),
+ mService.getBinderServiceInstance().goToSleep(mClock.now(),
PowerManager.GO_TO_SLEEP_REASON_APPLICATION, 0);
assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_DOZING);
}
@@ -550,7 +571,7 @@
int interval = 1000;
createService();
mService.onUserActivity();
- SystemClock.sleep(interval + 1 /* just a little more */);
+ advanceTime(interval + 1 /* just a little more */);
assertThat(mService.wasDeviceIdleForInternal(interval)).isTrue();
}
@@ -678,7 +699,7 @@
mService.getBinderServiceInstance().acquireWakeLock(token, flags, tag, packageName,
null /* workSource */, null /* historyTag */);
when(mDreamManagerInternalMock.isDreaming()).thenReturn(true);
- mService.getBinderServiceInstance().goToSleep(SystemClock.uptimeMillis(),
+ mService.getBinderServiceInstance().goToSleep(mClock.now(),
PowerManager.GO_TO_SLEEP_REASON_APPLICATION, 0);
assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_DOZING);
assertFalse(isAcquired[0]);
@@ -718,16 +739,16 @@
createService();
startSystem();
- mService.getBinderServiceInstance().userActivity(SystemClock.uptimeMillis(),
+ mService.getBinderServiceInstance().userActivity(mClock.now(),
PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0);
verify(mInattentiveSleepWarningControllerMock, never()).show();
- SystemClock.sleep(150);
+ advanceTime(150);
verify(mInattentiveSleepWarningControllerMock, times(1)).show();
verify(mInattentiveSleepWarningControllerMock, never()).dismiss(anyBoolean());
when(mInattentiveSleepWarningControllerMock.isShown()).thenReturn(true);
- mService.getBinderServiceInstance().userActivity(SystemClock.uptimeMillis(),
+ mService.getBinderServiceInstance().userActivity(mClock.now(),
PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0);
verify(mInattentiveSleepWarningControllerMock, times(1)).dismiss(true);
}
@@ -740,10 +761,10 @@
createService();
startSystem();
- SystemClock.sleep(50);
+ advanceTime(50);
verify(mInattentiveSleepWarningControllerMock, atLeastOnce()).show();
when(mInattentiveSleepWarningControllerMock.isShown()).thenReturn(true);
- SystemClock.sleep(70);
+ advanceTime(70);
assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_ASLEEP);
forceAwake();
assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
@@ -764,7 +785,7 @@
setAttentiveTimeout(5);
createService();
startSystem();
- SystemClock.sleep(20);
+ advanceTime(20);
assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_ASLEEP);
}
@@ -772,7 +793,7 @@
public void testInattentiveSleep_goesToSleepWithWakeLock() throws Exception {
final String pkg = mContextSpy.getOpPackageName();
final Binder token = new Binder();
- final String tag = "sleep_testWithWakeLock";
+ final String tag = "testInattentiveSleep_goesToSleepWithWakeLock";
setMinimumScreenOffTimeoutConfig(5);
setAttentiveTimeout(30);
@@ -783,7 +804,7 @@
PowerManager.SCREEN_BRIGHT_WAKE_LOCK, tag, pkg,
null /* workSource */, null /* historyTag */);
- SystemClock.sleep(60);
+ advanceTime(60);
assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_ASLEEP);
}
diff --git a/tests/utils/testutils/java/android/os/test/TestLooper.java b/tests/utils/testutils/java/android/os/test/TestLooper.java
index 01bd47b..a826646 100644
--- a/tests/utils/testutils/java/android/os/test/TestLooper.java
+++ b/tests/utils/testutils/java/android/os/test/TestLooper.java
@@ -48,6 +48,8 @@
private static final Method MESSAGE_MARK_IN_USE_METHOD;
private static final String TAG = "TestLooper";
+ private final Clock mClock;
+
private AutoDispatchThread mAutoDispatchThread;
static {
@@ -69,8 +71,25 @@
}
}
-
+ /**
+ * Creates a TestLooper and installs it as the looper for the current thread.
+ */
public TestLooper() {
+ this(SystemClock::uptimeMillis);
+ }
+
+ /**
+ * Creates a TestLooper with a custom clock and installs it as the looper for the current
+ * thread.
+ *
+ * Messages are dispatched when their {@link Message#when} is before or at {@link
+ * Clock#uptimeMillis()}.
+ * Use a custom clock with care. When using an offsettable clock like {@link
+ * com.android.server.testutils.OffsettableClock} be sure not to double offset messages by
+ * offsetting the clock and calling {@link #moveTimeForward(long)}. Instead, offset the clock
+ * and call {@link #dispatchAll()}.
+ */
+ public TestLooper(Clock clock) {
try {
mLooper = LOOPER_CONSTRUCTOR.newInstance(false);
@@ -80,6 +99,8 @@
} catch (IllegalAccessException | InstantiationException | InvocationTargetException e) {
throw new RuntimeException("Reflection error constructing or accessing looper", e);
}
+
+ mClock = clock;
}
public Looper getLooper() {
@@ -116,9 +137,13 @@
}
}
+ private long currentTime() {
+ return mClock.uptimeMillis();
+ }
+
private Message messageQueueNext() {
try {
- long now = SystemClock.uptimeMillis();
+ long now = currentTime();
Message prevMsg = null;
Message msg = getMessageLinkedList();
@@ -157,7 +182,7 @@
public synchronized boolean isIdle() {
Message messageList = getMessageLinkedList();
- return messageList != null && SystemClock.uptimeMillis() >= messageList.getWhen();
+ return messageList != null && currentTime() >= messageList.getWhen();
}
/**
@@ -187,6 +212,7 @@
/**
* Dispatch all messages currently in the queue
* Will not fail if there are no messages pending
+ *
* @return the number of messages dispatched
*/
public synchronized int dispatchAll() {
@@ -198,6 +224,10 @@
return count;
}
+ public interface Clock {
+ long uptimeMillis();
+ }
+
/**
* Thread used to dispatch messages when the main thread is blocked waiting for a response.
*/