summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/core/java/com/android/server/DeviceIdleController.java46
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java68
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