diff options
| -rw-r--r-- | services/core/java/com/android/server/DeviceIdleController.java | 46 | ||||
| -rw-r--r-- | services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java | 68 |
2 files changed, 107 insertions, 7 deletions
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java index ddfc3a631be6..a30371876955 100644 --- a/services/core/java/com/android/server/DeviceIdleController.java +++ b/services/core/java/com/android/server/DeviceIdleController.java @@ -1742,6 +1742,12 @@ public class DeviceIdleController extends SystemService return mConstants; } + + /** Returns the current elapsed realtime in milliseconds. */ + long getElapsedRealtime() { + return SystemClock.elapsedRealtime(); + } + LocationManager getLocationManager() { if (mLocationManager == null) { mLocationManager = mContext.getSystemService(LocationManager.class); @@ -2023,7 +2029,7 @@ public class DeviceIdleController extends SystemService private void unregisterDeviceIdleConstraintInternal(IDeviceIdleConstraint constraint) { synchronized (this) { - // Artifically force the constraint to inactive to unblock anything waiting for it. + // Artificially force the constraint to inactive to unblock anything waiting for it. onConstraintStateChangedLocked(constraint, /* active= */ false); // Let the constraint know that we are not listening to it any more. @@ -2746,9 +2752,18 @@ public class DeviceIdleController extends SystemService mState = STATE_QUICK_DOZE_DELAY; // Make sure any motion sensing or locating is stopped. resetIdleManagementLocked(); - // Wait a small amount of time in case something (eg: background service from - // recently closed app) needs to finish running. - scheduleAlarmLocked(mConstants.QUICK_DOZE_DELAY_TIMEOUT, false); + if (isUpcomingAlarmClock()) { + // If there's an upcoming AlarmClock alarm, we won't go into idle, so + // setting a wakeup alarm before the upcoming alarm is futile. Set the quick + // doze alarm to after the upcoming AlarmClock alarm. + scheduleAlarmLocked( + mAlarmManager.getNextWakeFromIdleTime() - mInjector.getElapsedRealtime() + + mConstants.QUICK_DOZE_DELAY_TIMEOUT, false); + } else { + // Wait a small amount of time in case something (eg: background service from + // recently closed app) needs to finish running. + scheduleAlarmLocked(mConstants.QUICK_DOZE_DELAY_TIMEOUT, false); + } EventLogTags.writeDeviceIdle(mState, "no activity"); } else if (mState == STATE_ACTIVE) { mState = STATE_INACTIVE; @@ -2758,7 +2773,16 @@ public class DeviceIdleController extends SystemService if (shouldUseIdleTimeoutFactorLocked()) { delay = (long) (mPreIdleFactor * delay); } - scheduleAlarmLocked(delay, false); + if (isUpcomingAlarmClock()) { + // If there's an upcoming AlarmClock alarm, we won't go into idle, so + // setting a wakeup alarm before the upcoming alarm is futile. Set the idle + // alarm to after the upcoming AlarmClock alarm. + scheduleAlarmLocked( + mAlarmManager.getNextWakeFromIdleTime() - mInjector.getElapsedRealtime() + + delay, false); + } else { + scheduleAlarmLocked(delay, false); + } EventLogTags.writeDeviceIdle(mState, "no activity"); } } @@ -2906,13 +2930,21 @@ public class DeviceIdleController extends SystemService return mState; } + /** + * Returns true if there's an upcoming AlarmClock alarm that is soon enough to prevent the + * device from going into idle. + */ + private boolean isUpcomingAlarmClock() { + return mInjector.getElapsedRealtime() + mConstants.MIN_TIME_TO_ALARM + >= mAlarmManager.getNextWakeFromIdleTime(); + } + @VisibleForTesting void stepIdleStateLocked(String reason) { if (DEBUG) Slog.d(TAG, "stepIdleStateLocked: mState=" + mState); EventLogTags.writeDeviceIdleStep(); - final long now = SystemClock.elapsedRealtime(); - if ((now+mConstants.MIN_TIME_TO_ALARM) > mAlarmManager.getNextWakeFromIdleTime()) { + if (isUpcomingAlarmClock()) { // Whoops, there is an upcoming alarm. We don't actually want to go idle. if (mState != STATE_ACTIVE) { mActiveReason = ACTIVE_REASON_ALARM; diff --git a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java index 06c83a26cca6..6feac520e538 100644 --- a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java @@ -53,6 +53,7 @@ import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.longThat; +import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; @@ -129,9 +130,12 @@ public class DeviceIdleControllerTest { ConnectivityService connectivityService; LocationManager locationManager; ConstraintController constraintController; + // Freeze time for testing. + long nowElapsed; InjectorForTest(Context ctx) { super(ctx); + nowElapsed = SystemClock.elapsedRealtime(); } @Override @@ -156,6 +160,11 @@ public class DeviceIdleControllerTest { } @Override + long getElapsedRealtime() { + return nowElapsed; + } + + @Override LocationManager getLocationManager() { return locationManager; } @@ -494,11 +503,44 @@ public class DeviceIdleControllerTest { mDeviceIdleController.becomeActiveLocked("testing", 0); verifyStateConditions(STATE_ACTIVE); + setAlarmSoon(false); + setChargingOn(false); + setScreenOn(false); + + mDeviceIdleController.becomeInactiveIfAppropriateLocked(); + verifyStateConditions(STATE_INACTIVE); + verify(mDeviceIdleController) + .scheduleAlarmLocked(eq(mConstants.INACTIVE_TIMEOUT), eq(false)); + } + + @Test + public void testStateActiveToStateInactive_UpcomingAlarm() { + final long timeUntilAlarm = mConstants.MIN_TIME_TO_ALARM / 2; + // Set an upcoming alarm that will prevent full idle. + doReturn(mInjector.getElapsedRealtime() + timeUntilAlarm) + .when(mAlarmManager).getNextWakeFromIdleTime(); + + InOrder inOrder = inOrder(mDeviceIdleController); + + enterDeepState(STATE_ACTIVE); + setQuickDozeEnabled(false); setChargingOn(false); setScreenOn(false); mDeviceIdleController.becomeInactiveIfAppropriateLocked(); verifyStateConditions(STATE_INACTIVE); + inOrder.verify(mDeviceIdleController) + .scheduleAlarmLocked(eq(timeUntilAlarm + mConstants.INACTIVE_TIMEOUT), eq(false)); + + enterDeepState(STATE_ACTIVE); + setQuickDozeEnabled(true); + setChargingOn(false); + setScreenOn(false); + + mDeviceIdleController.becomeInactiveIfAppropriateLocked(); + verifyStateConditions(STATE_QUICK_DOZE_DELAY); + inOrder.verify(mDeviceIdleController).scheduleAlarmLocked( + eq(timeUntilAlarm + mConstants.QUICK_DOZE_DELAY_TIMEOUT), eq(false)); } @Test @@ -515,42 +557,68 @@ public class DeviceIdleControllerTest { @Test public void testTransitionFromAnyStateToStateQuickDozeDelay() { + setAlarmSoon(false); + InOrder inOrder = inOrder(mDeviceIdleController); + enterDeepState(STATE_ACTIVE); setQuickDozeEnabled(true); setChargingOn(false); setScreenOn(false); verifyStateConditions(STATE_QUICK_DOZE_DELAY); + inOrder.verify(mDeviceIdleController) + .scheduleAlarmLocked(eq(mConstants.QUICK_DOZE_DELAY_TIMEOUT), eq(false)); enterDeepState(STATE_INACTIVE); setQuickDozeEnabled(true); verifyStateConditions(STATE_QUICK_DOZE_DELAY); + inOrder.verify(mDeviceIdleController) + .scheduleAlarmLocked(eq(mConstants.QUICK_DOZE_DELAY_TIMEOUT), eq(false)); enterDeepState(STATE_IDLE_PENDING); setQuickDozeEnabled(true); verifyStateConditions(STATE_QUICK_DOZE_DELAY); + inOrder.verify(mDeviceIdleController) + .scheduleAlarmLocked(eq(mConstants.QUICK_DOZE_DELAY_TIMEOUT), eq(false)); enterDeepState(STATE_SENSING); setQuickDozeEnabled(true); verifyStateConditions(STATE_QUICK_DOZE_DELAY); + inOrder.verify(mDeviceIdleController) + .scheduleAlarmLocked(eq(mConstants.QUICK_DOZE_DELAY_TIMEOUT), eq(false)); enterDeepState(STATE_LOCATING); setQuickDozeEnabled(true); verifyStateConditions(STATE_QUICK_DOZE_DELAY); + inOrder.verify(mDeviceIdleController) + .scheduleAlarmLocked(eq(mConstants.QUICK_DOZE_DELAY_TIMEOUT), eq(false)); // IDLE should stay as IDLE. enterDeepState(STATE_IDLE); + // Clear out any alarm setting from the order before checking for this section. + inOrder.verify(mDeviceIdleController, atLeastOnce()) + .scheduleAlarmLocked(anyLong(), anyBoolean()); setQuickDozeEnabled(true); verifyStateConditions(STATE_IDLE); + inOrder.verify(mDeviceIdleController, never()).scheduleAlarmLocked(anyLong(), anyBoolean()); // IDLE_MAINTENANCE should stay as IDLE_MAINTENANCE. enterDeepState(STATE_IDLE_MAINTENANCE); + // Clear out any alarm setting from the order before checking for this section. + inOrder.verify(mDeviceIdleController, atLeastOnce()) + .scheduleAlarmLocked(anyLong(), anyBoolean()); setQuickDozeEnabled(true); verifyStateConditions(STATE_IDLE_MAINTENANCE); + inOrder.verify(mDeviceIdleController, never()).scheduleAlarmLocked(anyLong(), anyBoolean()); + // State is already QUICK_DOZE_DELAY. No work should be done. enterDeepState(STATE_QUICK_DOZE_DELAY); + // Clear out any alarm setting from the order before checking for this section. + inOrder.verify(mDeviceIdleController, atLeastOnce()) + .scheduleAlarmLocked(anyLong(), anyBoolean()); setQuickDozeEnabled(true); mDeviceIdleController.becomeInactiveIfAppropriateLocked(); verifyStateConditions(STATE_QUICK_DOZE_DELAY); + inOrder.verify(mDeviceIdleController, never()).scheduleAlarmLocked(anyLong(), anyBoolean()); } @Test |