diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java index c706a3a54c382..823738347bac5 100644 --- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java +++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java @@ -25,6 +25,7 @@ import static android.os.Process.INVALID_UID; import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SuppressLint; import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.app.AlarmManager; @@ -127,28 +128,47 @@ import java.util.stream.Collectors;
digraph {
+ subgraph cluster_legend {
+ label="Legend"
+
+ wakeup_alarm [label="Entering this state requires a wakeup alarm",color=red,shape=box]
+ nonwakeup_alarm [
+ label="This state can be entered from a non-wakeup alarm",color=blue,shape=oval
+ ]
+ no_alarm [label="This state doesn't require an alarm",color=black,shape=diamond]
+ }
+
subgraph deep {
label="deep";
- STATE_ACTIVE [label="STATE_ACTIVE\nScreen on OR Charging OR Alarm going off soon"]
- STATE_INACTIVE [label="STATE_INACTIVE\nScreen off AND Not charging"]
+ STATE_ACTIVE [
+ label="STATE_ACTIVE\nScreen on OR Charging OR Alarm going off soon",
+ color=black,shape=diamond
+ ]
+ STATE_INACTIVE [
+ label="STATE_INACTIVE\nScreen off AND Not charging",color=black,shape=diamond
+ ]
STATE_QUICK_DOZE_DELAY [
label="STATE_QUICK_DOZE_DELAY\n"
+ "Screen off AND Not charging\n"
- + "Location, motion detection, and significant motion monitoring turned off"
+ + "Location, motion detection, and significant motion monitoring turned off",
+ color=black,shape=diamond
]
STATE_IDLE_PENDING [
- label="STATE_IDLE_PENDING\nSignificant motion monitoring turned on"
+ label="STATE_IDLE_PENDING\nSignificant motion monitoring turned on",
+ color=red,shape=box
]
- STATE_SENSING [label="STATE_SENSING\nMonitoring for ANY motion"]
+ STATE_SENSING [label="STATE_SENSING\nMonitoring for ANY motion",color=red,shape=box]
STATE_LOCATING [
- label="STATE_LOCATING\nRequesting location, motion monitoring still on"
+ label="STATE_LOCATING\nRequesting location, motion monitoring still on",
+ color=red,shape=box
]
STATE_IDLE [
label="STATE_IDLE\nLocation and motion detection turned off\n"
- + "Significant motion monitoring state unchanged"
+ + "Significant motion monitoring state unchanged",
+ color=red,shape=box
]
- STATE_IDLE_MAINTENANCE [label="STATE_IDLE_MAINTENANCE\n"]
+ STATE_IDLE_MAINTENANCE [label="STATE_IDLE_MAINTENANCE\n",color=red,shape=box]
STATE_ACTIVE -> STATE_INACTIVE [
label="becomeInactiveIfAppropriateLocked() AND Quick Doze not enabled"
@@ -213,19 +233,22 @@ import java.util.stream.Collectors;
label="light"
LIGHT_STATE_ACTIVE [
- label="LIGHT_STATE_ACTIVE\nScreen on OR Charging OR Alarm going off soon"
+ label="LIGHT_STATE_ACTIVE\nScreen on OR Charging OR Alarm going off soon",
+ color=black,shape=diamond
]
- LIGHT_STATE_INACTIVE [label="LIGHT_STATE_INACTIVE\nScreen off AND Not charging"]
- LIGHT_STATE_PRE_IDLE [
- label="LIGHT_STATE_PRE_IDLE\n"
- + "Delay going into LIGHT_STATE_IDLE due to some running jobs or alarms"
+ LIGHT_STATE_INACTIVE [
+ label="LIGHT_STATE_INACTIVE\nScreen off AND Not charging",
+ color=black,shape=diamond
]
- LIGHT_STATE_IDLE [label="LIGHT_STATE_IDLE\n"]
+ LIGHT_STATE_IDLE [label="LIGHT_STATE_IDLE\n",color=blue,shape=oval]
LIGHT_STATE_WAITING_FOR_NETWORK [
label="LIGHT_STATE_WAITING_FOR_NETWORK\n"
- + "Coming out of LIGHT_STATE_IDLE, waiting for network"
+ + "Coming out of LIGHT_STATE_IDLE, waiting for network",
+ color=black,shape=diamond
+ ]
+ LIGHT_STATE_IDLE_MAINTENANCE [
+ label="LIGHT_STATE_IDLE_MAINTENANCE\n",color=red,shape=box
]
- LIGHT_STATE_IDLE_MAINTENANCE [label="LIGHT_STATE_IDLE_MAINTENANCE\n"]
LIGHT_STATE_OVERRIDE [
label="LIGHT_STATE_OVERRIDE\nDevice in deep doze, light no longer changing states"
]
@@ -236,16 +259,9 @@ import java.util.stream.Collectors;
LIGHT_STATE_ACTIVE -> LIGHT_STATE_OVERRIDE [label="deep goes to STATE_IDLE"]
LIGHT_STATE_INACTIVE -> LIGHT_STATE_ACTIVE [label="becomeActiveLocked()"]
- LIGHT_STATE_INACTIVE -> LIGHT_STATE_PRE_IDLE [label="active jobs"]
- LIGHT_STATE_INACTIVE -> LIGHT_STATE_IDLE [label="no active jobs"]
+ LIGHT_STATE_INACTIVE -> LIGHT_STATE_IDLE [label="some time transpires"]
LIGHT_STATE_INACTIVE -> LIGHT_STATE_OVERRIDE [label="deep goes to STATE_IDLE"]
- LIGHT_STATE_PRE_IDLE -> LIGHT_STATE_ACTIVE [label="becomeActiveLocked()"]
- LIGHT_STATE_PRE_IDLE -> LIGHT_STATE_IDLE [
- label="stepLightIdleStateLocked(), exitMaintenanceEarlyIfNeededLocked()"
- ]
- LIGHT_STATE_PRE_IDLE -> LIGHT_STATE_OVERRIDE [label="deep goes to STATE_IDLE"]
-
LIGHT_STATE_IDLE -> LIGHT_STATE_ACTIVE [label="becomeActiveLocked()"]
LIGHT_STATE_IDLE -> LIGHT_STATE_WAITING_FOR_NETWORK [label="no network"]
LIGHT_STATE_IDLE -> LIGHT_STATE_IDLE_MAINTENANCE
@@ -421,9 +437,6 @@ public class DeviceIdleController extends SystemService
/** Device is inactive (screen off) and we are waiting to for the first light idle. */
@VisibleForTesting
static final int LIGHT_STATE_INACTIVE = 1;
- /** Device is about to go idle for the first time, wait for current work to complete. */
- @VisibleForTesting
- static final int LIGHT_STATE_PRE_IDLE = 3;
/** Device is in the light idle state, trying to stay asleep as much as possible. */
@VisibleForTesting
static final int LIGHT_STATE_IDLE = 4;
@@ -434,7 +447,7 @@ public class DeviceIdleController extends SystemService
/** Device is in the light idle state, but temporarily out of idle to do regular maintenance. */
@VisibleForTesting
static final int LIGHT_STATE_IDLE_MAINTENANCE = 6;
- /** Device light idle state is overriden, now applying deep doze state. */
+ /** Device light idle state is overridden, now applying deep doze state. */
@VisibleForTesting
static final int LIGHT_STATE_OVERRIDE = 7;
@@ -443,7 +456,6 @@ public class DeviceIdleController extends SystemService
switch (state) {
case LIGHT_STATE_ACTIVE: return "ACTIVE";
case LIGHT_STATE_INACTIVE: return "INACTIVE";
- case LIGHT_STATE_PRE_IDLE: return "PRE_IDLE";
case LIGHT_STATE_IDLE: return "IDLE";
case LIGHT_STATE_WAITING_FOR_NETWORK: return "WAITING_FOR_NETWORK";
case LIGHT_STATE_IDLE_MAINTENANCE: return "IDLE_MAINTENANCE";
@@ -468,10 +480,10 @@ public class DeviceIdleController extends SystemService
@GuardedBy("this")
private long mNextLightIdleDelay;
@GuardedBy("this")
- private long mNextLightIdleDelayFlex;
- @GuardedBy("this")
private long mNextLightAlarmTime;
@GuardedBy("this")
+ private long mNextLightMaintenanceAlarmTime;
+ @GuardedBy("this")
private long mNextSensingTimeoutAlarmTime;
/** How long a light idle maintenance window should last. */
@@ -658,13 +670,21 @@ public class DeviceIdleController extends SystemService
}
};
- private final AlarmManager.OnAlarmListener mLightAlarmListener
- = new AlarmManager.OnAlarmListener() {
- @Override
- public void onAlarm() {
- synchronized (DeviceIdleController.this) {
- stepLightIdleStateLocked("s:alarm");
- }
+ private final AlarmManager.OnAlarmListener mLightAlarmListener = () -> {
+ if (DEBUG) {
+ Slog.d(TAG, "Light progression alarm fired");
+ }
+ synchronized (DeviceIdleController.this) {
+ stepLightIdleStateLocked("s:alarm");
+ }
+ };
+
+ private final AlarmManager.OnAlarmListener mLightMaintenanceAlarmListener = () -> {
+ if (DEBUG) {
+ Slog.d(TAG, "Light maintenance alarm fired");
+ }
+ synchronized (DeviceIdleController.this) {
+ stepLightIdleStateLocked("s:alarm");
}
};
@@ -928,11 +948,7 @@ public class DeviceIdleController extends SystemService
private static final String KEY_FLEX_TIME_SHORT = "flex_time_short";
private static final String KEY_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT =
"light_after_inactive_to";
- private static final String KEY_LIGHT_PRE_IDLE_TIMEOUT = "light_pre_idle_to";
private static final String KEY_LIGHT_IDLE_TIMEOUT = "light_idle_to";
- private static final String KEY_LIGHT_IDLE_TIMEOUT_INITIAL_FLEX =
- "light_idle_to_initial_flex";
- private static final String KEY_LIGHT_MAX_IDLE_TIMEOUT_FLEX = "light_max_idle_to_flex";
private static final String KEY_LIGHT_IDLE_FACTOR = "light_idle_factor";
private static final String KEY_LIGHT_MAX_IDLE_TIMEOUT = "light_max_idle_to";
private static final String KEY_LIGHT_IDLE_MAINTENANCE_MIN_BUDGET =
@@ -978,15 +994,9 @@ public class DeviceIdleController extends SystemService
private static final long DEFAULT_FLEX_TIME_SHORT =
!COMPRESS_TIME ? 60 * 1000L : 5 * 1000L;
private static final long DEFAULT_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT =
- !COMPRESS_TIME ? 60 * 1000L : 15 * 1000L;
- private static final long DEFAULT_LIGHT_PRE_IDLE_TIMEOUT =
- !COMPRESS_TIME ? 3 * 60 * 1000L : 30 * 1000L;
+ !COMPRESS_TIME ? 4 * 60 * 1000L : 30 * 1000L;
private static final long DEFAULT_LIGHT_IDLE_TIMEOUT =
!COMPRESS_TIME ? 5 * 60 * 1000L : 15 * 1000L;
- private static final long DEFAULT_LIGHT_IDLE_TIMEOUT_INITIAL_FLEX =
- !COMPRESS_TIME ? 60 * 1000L : 5 * 1000L;
- private static final long DEFAULT_LIGHT_MAX_IDLE_TIMEOUT_FLEX =
- !COMPRESS_TIME ? 15 * 60 * 1000L : 60 * 1000L;
private static final float DEFAULT_LIGHT_IDLE_FACTOR = 2f;
private static final long DEFAULT_LIGHT_MAX_IDLE_TIMEOUT =
!COMPRESS_TIME ? 15 * 60 * 1000L : 60 * 1000L;
@@ -1053,15 +1063,6 @@ public class DeviceIdleController extends SystemService
*/
public long LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT = DEFAULT_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT;
- /**
- * This is amount of time we will wait from the point where we decide we would
- * like to go idle until we actually do, while waiting for jobs and other current
- * activity to finish.
- *
- * @see #KEY_LIGHT_PRE_IDLE_TIMEOUT
- */
- public long LIGHT_PRE_IDLE_TIMEOUT = DEFAULT_LIGHT_PRE_IDLE_TIMEOUT;
-
/**
* This is the initial time that we will run in light idle maintenance mode.
*
@@ -1069,21 +1070,6 @@ public class DeviceIdleController extends SystemService
*/
public long LIGHT_IDLE_TIMEOUT = DEFAULT_LIGHT_IDLE_TIMEOUT;
- /**
- * This is the initial alarm window size that we will tolerate for light idle maintenance
- * timing.
- *
- * @see #KEY_LIGHT_IDLE_TIMEOUT_INITIAL_FLEX
- */
- public long LIGHT_IDLE_TIMEOUT_INITIAL_FLEX = DEFAULT_LIGHT_IDLE_TIMEOUT_INITIAL_FLEX;
-
- /**
- * This is the maximum value that {@link #LIGHT_IDLE_TIMEOUT_INITIAL_FLEX} should take.
- *
- * @see #KEY_LIGHT_MAX_IDLE_TIMEOUT_FLEX
- */
- public long LIGHT_MAX_IDLE_TIMEOUT_FLEX = DEFAULT_LIGHT_MAX_IDLE_TIMEOUT_FLEX;
-
/**
* Scaling factor to apply to the light idle mode time each time we complete a cycle.
*
@@ -1327,24 +1313,10 @@ public class DeviceIdleController extends SystemService
KEY_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT,
DEFAULT_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT);
break;
- case KEY_LIGHT_PRE_IDLE_TIMEOUT:
- LIGHT_PRE_IDLE_TIMEOUT = properties.getLong(
- KEY_LIGHT_PRE_IDLE_TIMEOUT, DEFAULT_LIGHT_PRE_IDLE_TIMEOUT);
- break;
case KEY_LIGHT_IDLE_TIMEOUT:
LIGHT_IDLE_TIMEOUT = properties.getLong(
KEY_LIGHT_IDLE_TIMEOUT, DEFAULT_LIGHT_IDLE_TIMEOUT);
break;
- case KEY_LIGHT_IDLE_TIMEOUT_INITIAL_FLEX:
- LIGHT_IDLE_TIMEOUT_INITIAL_FLEX = properties.getLong(
- KEY_LIGHT_IDLE_TIMEOUT_INITIAL_FLEX,
- DEFAULT_LIGHT_IDLE_TIMEOUT_INITIAL_FLEX);
- break;
- case KEY_LIGHT_MAX_IDLE_TIMEOUT_FLEX:
- LIGHT_MAX_IDLE_TIMEOUT_FLEX = properties.getLong(
- KEY_LIGHT_MAX_IDLE_TIMEOUT_FLEX,
- DEFAULT_LIGHT_MAX_IDLE_TIMEOUT_FLEX);
- break;
case KEY_LIGHT_IDLE_FACTOR:
LIGHT_IDLE_FACTOR = Math.max(1, properties.getFloat(
KEY_LIGHT_IDLE_FACTOR, DEFAULT_LIGHT_IDLE_FACTOR));
@@ -1497,22 +1469,10 @@ public class DeviceIdleController extends SystemService
TimeUtils.formatDuration(LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT, pw);
pw.println();
- pw.print(" "); pw.print(KEY_LIGHT_PRE_IDLE_TIMEOUT); pw.print("=");
- TimeUtils.formatDuration(LIGHT_PRE_IDLE_TIMEOUT, pw);
- pw.println();
-
pw.print(" "); pw.print(KEY_LIGHT_IDLE_TIMEOUT); pw.print("=");
TimeUtils.formatDuration(LIGHT_IDLE_TIMEOUT, pw);
pw.println();
- pw.print(" "); pw.print(KEY_LIGHT_IDLE_TIMEOUT_INITIAL_FLEX); pw.print("=");
- TimeUtils.formatDuration(LIGHT_IDLE_TIMEOUT_INITIAL_FLEX, pw);
- pw.println();
-
- pw.print(" "); pw.print(KEY_LIGHT_MAX_IDLE_TIMEOUT_FLEX); pw.print("=");
- TimeUtils.formatDuration(LIGHT_MAX_IDLE_TIMEOUT_FLEX, pw);
- pw.println();
-
pw.print(" "); pw.print(KEY_LIGHT_IDLE_FACTOR); pw.print("=");
pw.print(LIGHT_IDLE_FACTOR);
pw.println();
@@ -3088,7 +3048,7 @@ public class DeviceIdleController extends SystemService
if (conn != mNetworkConnected) {
mNetworkConnected = conn;
if (conn && mLightState == LIGHT_STATE_WAITING_FOR_NETWORK) {
- stepLightIdleStateLocked("network");
+ stepLightIdleStateLocked("network", /* forceProgression */ true);
}
}
}
@@ -3343,7 +3303,11 @@ public class DeviceIdleController extends SystemService
if (DEBUG) Slog.d(TAG, "Moved from LIGHT_STATE_ACTIVE to LIGHT_STATE_INACTIVE");
resetLightIdleManagementLocked();
scheduleLightAlarmLocked(mConstants.LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT,
- mConstants.FLEX_TIME_SHORT, true);
+ mConstants.FLEX_TIME_SHORT);
+ // After moving in INACTIVE, the maintenance window should start the time inactive
+ // timeout and a single light idle period.
+ scheduleLightMaintenanceAlarmLocked(
+ mConstants.LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT + mConstants.LIGHT_IDLE_TIMEOUT);
EventLogTags.writeDeviceIdleLight(mLightState, "no activity");
}
}
@@ -3364,9 +3328,9 @@ public class DeviceIdleController extends SystemService
@GuardedBy("this")
private void resetLightIdleManagementLocked() {
- mNextLightIdleDelay = 0;
- mNextLightIdleDelayFlex = 0;
- mCurLightIdleBudget = 0;
+ mNextLightIdleDelay = mConstants.LIGHT_IDLE_TIMEOUT;
+ mMaintenanceStartTime = 0;
+ mCurLightIdleBudget = mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET;
cancelLightAlarmLocked();
}
@@ -3401,90 +3365,115 @@ public class DeviceIdleController extends SystemService
}
@GuardedBy("this")
- void stepLightIdleStateLocked(String reason) {
- if (mLightState == LIGHT_STATE_OVERRIDE) {
+ private void stepLightIdleStateLocked(String reason) {
+ stepLightIdleStateLocked(reason, false);
+ }
+
+ @GuardedBy("this")
+ @VisibleForTesting
+ @SuppressLint("WakelockTimeout")
+ void stepLightIdleStateLocked(String reason, boolean forceProgression) {
+ if (mLightState == LIGHT_STATE_ACTIVE || mLightState == LIGHT_STATE_OVERRIDE) {
// If we are already in deep device idle mode, then
// there is nothing left to do for light mode.
return;
}
- if (DEBUG) Slog.d(TAG, "stepLightIdleStateLocked: mLightState=" + mLightState);
+ if (DEBUG) {
+ Slog.d(TAG, "stepLightIdleStateLocked: mLightState=" + lightStateToString(mLightState)
+ + " force=" + forceProgression);
+ }
EventLogTags.writeDeviceIdleLightStep();
- switch (mLightState) {
- case LIGHT_STATE_INACTIVE:
- mCurLightIdleBudget = mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET;
- // Reset the upcoming idle delays.
- mNextLightIdleDelay = mConstants.LIGHT_IDLE_TIMEOUT;
- mNextLightIdleDelayFlex = mConstants.LIGHT_IDLE_TIMEOUT_INITIAL_FLEX;
- mMaintenanceStartTime = 0;
- if (!isOpsInactiveLocked()) {
- // We have some active ops going on... give them a chance to finish
- // before going in to our first idle.
- mLightState = LIGHT_STATE_PRE_IDLE;
- EventLogTags.writeDeviceIdleLight(mLightState, reason);
- scheduleLightAlarmLocked(mConstants.LIGHT_PRE_IDLE_TIMEOUT,
- mConstants.FLEX_TIME_SHORT, true);
- break;
+ final long nowElapsed = mInjector.getElapsedRealtime();
+ final boolean crossedMaintenanceTime =
+ mNextLightMaintenanceAlarmTime > 0 && nowElapsed >= mNextLightMaintenanceAlarmTime;
+ final boolean crossedProgressionTime =
+ mNextLightAlarmTime > 0 && nowElapsed >= mNextLightAlarmTime;
+ final boolean enterMaintenance;
+ if (crossedMaintenanceTime) {
+ if (crossedProgressionTime) {
+ enterMaintenance = (mNextLightAlarmTime <= mNextLightMaintenanceAlarmTime);
+ } else {
+ enterMaintenance = true;
+ }
+ } else if (crossedProgressionTime) {
+ enterMaintenance = false;
+ } else if (forceProgression) {
+ // This will happen for adb commands, unit tests,
+ // and when we're in WAITING_FOR_NETWORK and the network connects.
+ enterMaintenance =
+ mLightState == LIGHT_STATE_IDLE
+ || mLightState == LIGHT_STATE_WAITING_FOR_NETWORK;
+ } else {
+ Slog.wtfStack(TAG, "stepLightIdleStateLocked called in invalid state");
+ return;
+ }
+
+ if (enterMaintenance) {
+ if (mNetworkConnected || mLightState == LIGHT_STATE_WAITING_FOR_NETWORK) {
+ // We have been idling long enough, now it is time to do some work.
+ mActiveIdleOpCount = 1;
+ mActiveIdleWakeLock.acquire();
+ mMaintenanceStartTime = SystemClock.elapsedRealtime();
+ if (mCurLightIdleBudget < mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET) {
+ mCurLightIdleBudget = mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET;
+ } else if (mCurLightIdleBudget > mConstants.LIGHT_IDLE_MAINTENANCE_MAX_BUDGET) {
+ mCurLightIdleBudget = mConstants.LIGHT_IDLE_MAINTENANCE_MAX_BUDGET;
}
- // Nothing active, fall through to immediately idle.
- case LIGHT_STATE_PRE_IDLE:
- case LIGHT_STATE_IDLE_MAINTENANCE:
- if (mMaintenanceStartTime != 0) {
- long duration = SystemClock.elapsedRealtime() - mMaintenanceStartTime;
- if (duration < mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET) {
- // We didn't use up all of our minimum budget; add this to the reserve.
- mCurLightIdleBudget +=
- (mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET - duration);
- } else {
- // We used more than our minimum budget; this comes out of the reserve.
- mCurLightIdleBudget -=
- (duration - mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET);
- }
- }
- mMaintenanceStartTime = 0;
- scheduleLightAlarmLocked(mNextLightIdleDelay, mNextLightIdleDelayFlex, false);
mNextLightIdleDelay = Math.min(mConstants.LIGHT_MAX_IDLE_TIMEOUT,
(long) (mNextLightIdleDelay * mConstants.LIGHT_IDLE_FACTOR));
- mNextLightIdleDelayFlex = Math.min(mConstants.LIGHT_MAX_IDLE_TIMEOUT_FLEX,
- (long) (mNextLightIdleDelayFlex * mConstants.LIGHT_IDLE_FACTOR));
- if (DEBUG) Slog.d(TAG, "Moved to LIGHT_STATE_IDLE.");
- mLightState = LIGHT_STATE_IDLE;
- EventLogTags.writeDeviceIdleLight(mLightState, reason);
- addEvent(EVENT_LIGHT_IDLE, null);
- mGoingIdleWakeLock.acquire();
- mHandler.sendEmptyMessage(MSG_REPORT_IDLE_ON_LIGHT);
- break;
- case LIGHT_STATE_IDLE:
- case LIGHT_STATE_WAITING_FOR_NETWORK:
- if (mNetworkConnected || mLightState == LIGHT_STATE_WAITING_FOR_NETWORK) {
- // We have been idling long enough, now it is time to do some work.
- mActiveIdleOpCount = 1;
- mActiveIdleWakeLock.acquire();
- mMaintenanceStartTime = SystemClock.elapsedRealtime();
- if (mCurLightIdleBudget < mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET) {
- mCurLightIdleBudget = mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET;
- } else if (mCurLightIdleBudget > mConstants.LIGHT_IDLE_MAINTENANCE_MAX_BUDGET) {
- mCurLightIdleBudget = mConstants.LIGHT_IDLE_MAINTENANCE_MAX_BUDGET;
- }
- scheduleLightAlarmLocked(mCurLightIdleBudget, mConstants.FLEX_TIME_SHORT, true);
- if (DEBUG) Slog.d(TAG,
- "Moved from LIGHT_STATE_IDLE to LIGHT_STATE_IDLE_MAINTENANCE.");
- mLightState = LIGHT_STATE_IDLE_MAINTENANCE;
- EventLogTags.writeDeviceIdleLight(mLightState, reason);
- addEvent(EVENT_LIGHT_MAINTENANCE, null);
- mHandler.sendEmptyMessage(MSG_REPORT_IDLE_OFF);
- } else {
- // We'd like to do maintenance, but currently don't have network
- // connectivity... let's try to wait until the network comes back.
- // We'll only wait for another full idle period, however, and then give up.
- scheduleLightAlarmLocked(mNextLightIdleDelay,
- mNextLightIdleDelayFlex / 2, true);
- if (DEBUG) Slog.d(TAG, "Moved to LIGHT_WAITING_FOR_NETWORK.");
- mLightState = LIGHT_STATE_WAITING_FOR_NETWORK;
- EventLogTags.writeDeviceIdleLight(mLightState, reason);
+ // We're entering MAINTENANCE. It should end curLightIdleBudget time from now.
+ // The next maintenance window should be curLightIdleBudget + nextLightIdleDelay
+ // time from now.
+ scheduleLightAlarmLocked(mCurLightIdleBudget, mConstants.FLEX_TIME_SHORT);
+ scheduleLightMaintenanceAlarmLocked(mCurLightIdleBudget + mNextLightIdleDelay);
+ if (DEBUG) {
+ Slog.d(TAG, "Moved from " + lightStateToString(mLightState)
+ + " to LIGHT_STATE_IDLE_MAINTENANCE");
}
- break;
+ mLightState = LIGHT_STATE_IDLE_MAINTENANCE;
+ EventLogTags.writeDeviceIdleLight(mLightState, reason);
+ addEvent(EVENT_LIGHT_MAINTENANCE, null);
+ mHandler.sendEmptyMessage(MSG_REPORT_IDLE_OFF);
+ } else {
+ // We'd like to do maintenance, but currently don't have network
+ // connectivity... let's try to wait until the network comes back.
+ // We'll only wait for another full idle period, however, and then give up.
+ scheduleLightMaintenanceAlarmLocked(mNextLightIdleDelay);
+ mNextLightAlarmTime = 0;
+ if (DEBUG) Slog.d(TAG, "Moved to LIGHT_WAITING_FOR_NETWORK.");
+ mLightState = LIGHT_STATE_WAITING_FOR_NETWORK;
+ EventLogTags.writeDeviceIdleLight(mLightState, reason);
+ }
+ } else {
+ if (mMaintenanceStartTime != 0) {
+ // Cap duration at budget since the non-wakeup alarm to exit maintenance may
+ // not fire at the exact intended time, but once the system is up, we will stop
+ // more ongoing work.
+ long duration = Math.min(mCurLightIdleBudget,
+ SystemClock.elapsedRealtime() - mMaintenanceStartTime);
+ if (duration < mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET) {
+ // We didn't use up all of our minimum budget; add this to the reserve.
+ mCurLightIdleBudget +=
+ (mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET - duration);
+ } else {
+ // We used more than our minimum budget; this comes out of the reserve.
+ mCurLightIdleBudget -=
+ (duration - mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET);
+ }
+ }
+ mMaintenanceStartTime = 0;
+ // We're entering IDLE. We may have used less than curLightIdleBudget for the
+ // maintenance window, so reschedule the alarm starting from now.
+ scheduleLightMaintenanceAlarmLocked(mNextLightIdleDelay);
+ mNextLightAlarmTime = 0;
+ if (DEBUG) Slog.d(TAG, "Moved to LIGHT_STATE_IDLE.");
+ mLightState = LIGHT_STATE_IDLE;
+ EventLogTags.writeDeviceIdleLight(mLightState, reason);
+ addEvent(EVENT_LIGHT_IDLE, null);
+ mGoingIdleWakeLock.acquire();
+ mHandler.sendEmptyMessage(MSG_REPORT_IDLE_ON_LIGHT);
}
}
@@ -3839,8 +3828,7 @@ public class DeviceIdleController extends SystemService
@GuardedBy("this")
void exitMaintenanceEarlyIfNeededLocked() {
- if (mState == STATE_IDLE_MAINTENANCE || mLightState == LIGHT_STATE_IDLE_MAINTENANCE
- || mLightState == LIGHT_STATE_PRE_IDLE) {
+ if (mState == STATE_IDLE_MAINTENANCE || mLightState == LIGHT_STATE_IDLE_MAINTENANCE) {
if (isOpsInactiveLocked()) {
final long now = SystemClock.elapsedRealtime();
if (DEBUG) {
@@ -3853,10 +3841,8 @@ public class DeviceIdleController extends SystemService
}
if (mState == STATE_IDLE_MAINTENANCE) {
stepIdleStateLocked("s:early");
- } else if (mLightState == LIGHT_STATE_PRE_IDLE) {
- stepLightIdleStateLocked("s:predone");
} else {
- stepLightIdleStateLocked("s:early");
+ stepLightIdleStateLocked("s:early", /* forceProgression */ true);
}
}
}
@@ -3969,6 +3955,10 @@ public class DeviceIdleController extends SystemService
mNextLightAlarmTime = 0;
mAlarmManager.cancel(mLightAlarmListener);
}
+ if (mNextLightMaintenanceAlarmTime != 0) {
+ mNextLightMaintenanceAlarmTime = 0;
+ mAlarmManager.cancel(mLightMaintenanceAlarmListener);
+ }
}
@GuardedBy("this")
@@ -4035,26 +4025,54 @@ public class DeviceIdleController extends SystemService
}
@GuardedBy("this")
- void scheduleLightAlarmLocked(long delay, long flex, boolean wakeup) {
+ @VisibleForTesting
+ void scheduleLightAlarmLocked(long delay, long flex) {
if (DEBUG) {
Slog.d(TAG, "scheduleLightAlarmLocked(" + delay
+ (mConstants.USE_WINDOW_ALARMS ? "/" + flex : "")
- + ", wakeup=" + wakeup + ")");
+ + ")");
}
- mNextLightAlarmTime = SystemClock.elapsedRealtime() + delay;
+ mNextLightAlarmTime = mInjector.getElapsedRealtime() + delay;
if (mConstants.USE_WINDOW_ALARMS) {
mAlarmManager.setWindow(
- wakeup ? AlarmManager.ELAPSED_REALTIME_WAKEUP : AlarmManager.ELAPSED_REALTIME,
+ AlarmManager.ELAPSED_REALTIME,
mNextLightAlarmTime, flex,
"DeviceIdleController.light", mLightAlarmListener, mHandler);
} else {
mAlarmManager.set(
- wakeup ? AlarmManager.ELAPSED_REALTIME_WAKEUP : AlarmManager.ELAPSED_REALTIME,
+ AlarmManager.ELAPSED_REALTIME,
mNextLightAlarmTime,
"DeviceIdleController.light", mLightAlarmListener, mHandler);
}
}
+ @GuardedBy("this")
+ @VisibleForTesting
+ void scheduleLightMaintenanceAlarmLocked(long delay) {
+ if (DEBUG) {
+ Slog.d(TAG, "scheduleLightMaintenanceAlarmLocked(" + delay + ")");
+ }
+ mNextLightMaintenanceAlarmTime = mInjector.getElapsedRealtime() + delay;
+ mAlarmManager.setWindow(
+ AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ mNextLightMaintenanceAlarmTime, mConstants.FLEX_TIME_SHORT,
+ "DeviceIdleController.light", mLightMaintenanceAlarmListener, mHandler);
+ }
+
+ @VisibleForTesting
+ long getNextLightAlarmTimeForTesting() {
+ synchronized (this) {
+ return mNextLightAlarmTime;
+ }
+ }
+
+ @VisibleForTesting
+ long getNextLightMaintenanceAlarmTimeForTesting() {
+ synchronized (this) {
+ return mNextLightMaintenanceAlarmTime;
+ }
+ }
+
private void scheduleMotionRegistrationAlarmLocked() {
if (DEBUG) Slog.d(TAG, "scheduleMotionRegistrationAlarmLocked");
long nextMotionRegistrationAlarmTime =
@@ -4424,7 +4442,7 @@ public class DeviceIdleController extends SystemService
pw.print("Stepped to deep: ");
pw.println(stateToString(mState));
} else if ("light".equals(arg)) {
- stepLightIdleStateLocked("s:shell");
+ stepLightIdleStateLocked("s:shell", /* forceProgression */ true);
pw.print("Stepped to light: "); pw.println(lightStateToString(mLightState));
} else {
pw.println("Unknown idle mode: " + arg);
@@ -4464,7 +4482,7 @@ public class DeviceIdleController extends SystemService
becomeInactiveIfAppropriateLocked();
int curLightState = mLightState;
while (curLightState != LIGHT_STATE_IDLE) {
- stepLightIdleStateLocked("s:shell");
+ stepLightIdleStateLocked("s:shell", /* forceProgression */ true);
if (curLightState == mLightState) {
pw.print("Unable to go light idle; stopped at ");
pw.println(lightStateToString(mLightState));
@@ -5076,19 +5094,19 @@ public class DeviceIdleController extends SystemService
if (mNextLightIdleDelay != 0) {
pw.print(" mNextLightIdleDelay=");
TimeUtils.formatDuration(mNextLightIdleDelay, pw);
- if (mConstants.USE_WINDOW_ALARMS) {
- pw.print(" (flex=");
- TimeUtils.formatDuration(mNextLightIdleDelayFlex, pw);
- pw.println(")");
- } else {
- pw.println();
- }
+ pw.println();
}
if (mNextLightAlarmTime != 0) {
pw.print(" mNextLightAlarmTime=");
TimeUtils.formatDuration(mNextLightAlarmTime, SystemClock.elapsedRealtime(), pw);
pw.println();
}
+ if (mNextLightMaintenanceAlarmTime != 0) {
+ pw.print(" mNextLightMaintenanceAlarmTime=");
+ TimeUtils.formatDuration(
+ mNextLightMaintenanceAlarmTime, SystemClock.elapsedRealtime(), pw);
+ pw.println();
+ }
if (mCurLightIdleBudget != 0) {
pw.print(" mCurLightIdleBudget=");
TimeUtils.formatDuration(mCurLightIdleBudget, pw);
diff --git a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
index d71030802c2b9..ac115a26f9e47 100644
--- a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
@@ -30,7 +30,6 @@ import static com.android.server.DeviceIdleController.LIGHT_STATE_IDLE;
import static com.android.server.DeviceIdleController.LIGHT_STATE_IDLE_MAINTENANCE;
import static com.android.server.DeviceIdleController.LIGHT_STATE_INACTIVE;
import static com.android.server.DeviceIdleController.LIGHT_STATE_OVERRIDE;
-import static com.android.server.DeviceIdleController.LIGHT_STATE_PRE_IDLE;
import static com.android.server.DeviceIdleController.LIGHT_STATE_WAITING_FOR_NETWORK;
import static com.android.server.DeviceIdleController.MSG_REPORT_STATIONARY_STATUS;
import static com.android.server.DeviceIdleController.MSG_RESET_PRE_IDLE_TIMEOUT_FACTOR;
@@ -112,6 +111,7 @@ import java.util.concurrent.Executor;
/**
* Tests for {@link com.android.server.DeviceIdleController}.
*/
+@SuppressWarnings("GuardedBy")
@RunWith(AndroidJUnit4.class)
public class DeviceIdleControllerTest {
private DeviceIdleController mDeviceIdleController;
@@ -875,7 +875,7 @@ public class DeviceIdleControllerTest {
@Test
public void testLightStepIdleStateLocked_InvalidStates() {
mDeviceIdleController.becomeActiveLocked("testing", 0);
- mDeviceIdleController.stepLightIdleStateLocked("testing");
+ mDeviceIdleController.stepLightIdleStateLocked("testing", true);
// stepLightIdleStateLocked doesn't handle the ACTIVE case, so the state
// should stay as ACTIVE.
verifyLightStateConditions(LIGHT_STATE_ACTIVE);
@@ -888,7 +888,7 @@ public class DeviceIdleControllerTest {
@Test
public void testLightStepIdleStateLocked_Overriden() {
enterLightState(LIGHT_STATE_OVERRIDE);
- mDeviceIdleController.stepLightIdleStateLocked("testing");
+ mDeviceIdleController.stepLightIdleStateLocked("testing", true);
verifyLightStateConditions(LIGHT_STATE_OVERRIDE);
}
@@ -906,18 +906,18 @@ public class DeviceIdleControllerTest {
verifyLightStateConditions(LIGHT_STATE_INACTIVE);
// No active ops means INACTIVE should go straight to IDLE.
- mDeviceIdleController.stepLightIdleStateLocked("testing");
+ mDeviceIdleController.stepLightIdleStateLocked("testing", true);
verifyLightStateConditions(LIGHT_STATE_IDLE);
// Should just alternate between IDLE and IDLE_MAINTENANCE now.
- mDeviceIdleController.stepLightIdleStateLocked("testing");
+ mDeviceIdleController.stepLightIdleStateLocked("testing", true);
verifyLightStateConditions(LIGHT_STATE_IDLE_MAINTENANCE);
- mDeviceIdleController.stepLightIdleStateLocked("testing");
+ mDeviceIdleController.stepLightIdleStateLocked("testing", true);
verifyLightStateConditions(LIGHT_STATE_IDLE);
- mDeviceIdleController.stepLightIdleStateLocked("testing");
+ mDeviceIdleController.stepLightIdleStateLocked("testing", true);
verifyLightStateConditions(LIGHT_STATE_IDLE_MAINTENANCE);
}
@@ -930,26 +930,22 @@ public class DeviceIdleControllerTest {
setScreenOn(false);
verifyLightStateConditions(LIGHT_STATE_INACTIVE);
- // Active ops means INACTIVE should go to PRE_IDLE to wait.
+ // After enough time, INACTIVE should go to IDLE regardless of any active ops.
mDeviceIdleController.setJobsActive(true);
mDeviceIdleController.setAlarmsActive(true);
mDeviceIdleController.setActiveIdleOpsForTest(1);
- mDeviceIdleController.stepLightIdleStateLocked("testing");
- verifyLightStateConditions(LIGHT_STATE_PRE_IDLE);
-
- // Even with active ops, PRE_IDLE should go to IDLE.
- mDeviceIdleController.stepLightIdleStateLocked("testing");
+ mDeviceIdleController.stepLightIdleStateLocked("testing", true);
verifyLightStateConditions(LIGHT_STATE_IDLE);
// Should just alternate between IDLE and IDLE_MAINTENANCE now.
- mDeviceIdleController.stepLightIdleStateLocked("testing");
+ mDeviceIdleController.stepLightIdleStateLocked("testing", true);
verifyLightStateConditions(LIGHT_STATE_IDLE_MAINTENANCE);
- mDeviceIdleController.stepLightIdleStateLocked("testing");
+ mDeviceIdleController.stepLightIdleStateLocked("testing", true);
verifyLightStateConditions(LIGHT_STATE_IDLE);
- mDeviceIdleController.stepLightIdleStateLocked("testing");
+ mDeviceIdleController.stepLightIdleStateLocked("testing", true);
verifyLightStateConditions(LIGHT_STATE_IDLE_MAINTENANCE);
}
@@ -967,24 +963,24 @@ public class DeviceIdleControllerTest {
verifyLightStateConditions(LIGHT_STATE_INACTIVE);
// No active ops means INACTIVE should go straight to IDLE.
- mDeviceIdleController.stepLightIdleStateLocked("testing");
+ mDeviceIdleController.stepLightIdleStateLocked("testing", true);
verifyLightStateConditions(LIGHT_STATE_IDLE);
// Should cycle between IDLE, WAITING_FOR_NETWORK, and IDLE_MAINTENANCE now.
- mDeviceIdleController.stepLightIdleStateLocked("testing");
+ mDeviceIdleController.stepLightIdleStateLocked("testing", true);
verifyLightStateConditions(LIGHT_STATE_WAITING_FOR_NETWORK);
- mDeviceIdleController.stepLightIdleStateLocked("testing");
+ mDeviceIdleController.stepLightIdleStateLocked("testing", true);
verifyLightStateConditions(LIGHT_STATE_IDLE_MAINTENANCE);
- mDeviceIdleController.stepLightIdleStateLocked("testing");
+ mDeviceIdleController.stepLightIdleStateLocked("testing", true);
verifyLightStateConditions(LIGHT_STATE_IDLE);
- mDeviceIdleController.stepLightIdleStateLocked("testing");
+ mDeviceIdleController.stepLightIdleStateLocked("testing", true);
verifyLightStateConditions(LIGHT_STATE_WAITING_FOR_NETWORK);
- mDeviceIdleController.stepLightIdleStateLocked("testing");
+ mDeviceIdleController.stepLightIdleStateLocked("testing", true);
verifyLightStateConditions(LIGHT_STATE_IDLE_MAINTENANCE);
}
@@ -997,35 +993,176 @@ public class DeviceIdleControllerTest {
setScreenOn(false);
verifyLightStateConditions(LIGHT_STATE_INACTIVE);
- // Active ops means INACTIVE should go to PRE_IDLE to wait.
+ // After enough time, INACTIVE should go to IDLE regardless of any active ops.
mDeviceIdleController.setJobsActive(true);
mDeviceIdleController.setAlarmsActive(true);
mDeviceIdleController.setActiveIdleOpsForTest(1);
- mDeviceIdleController.stepLightIdleStateLocked("testing");
- verifyLightStateConditions(LIGHT_STATE_PRE_IDLE);
-
- // Even with active ops, PRE_IDLE should go to IDLE.
- mDeviceIdleController.stepLightIdleStateLocked("testing");
+ mDeviceIdleController.stepLightIdleStateLocked("testing", true);
verifyLightStateConditions(LIGHT_STATE_IDLE);
// Should cycle between IDLE, WAITING_FOR_NETWORK, and IDLE_MAINTENANCE now.
- mDeviceIdleController.stepLightIdleStateLocked("testing");
+ mDeviceIdleController.stepLightIdleStateLocked("testing", true);
verifyLightStateConditions(LIGHT_STATE_WAITING_FOR_NETWORK);
- mDeviceIdleController.stepLightIdleStateLocked("testing");
+ mDeviceIdleController.stepLightIdleStateLocked("testing", true);
verifyLightStateConditions(LIGHT_STATE_IDLE_MAINTENANCE);
- mDeviceIdleController.stepLightIdleStateLocked("testing");
+ mDeviceIdleController.stepLightIdleStateLocked("testing", true);
verifyLightStateConditions(LIGHT_STATE_IDLE);
- mDeviceIdleController.stepLightIdleStateLocked("testing");
+ mDeviceIdleController.stepLightIdleStateLocked("testing", true);
verifyLightStateConditions(LIGHT_STATE_WAITING_FOR_NETWORK);
- mDeviceIdleController.stepLightIdleStateLocked("testing");
+ mDeviceIdleController.stepLightIdleStateLocked("testing", true);
verifyLightStateConditions(LIGHT_STATE_IDLE_MAINTENANCE);
}
+ @Test
+ public void testLightStepIdleStateSkippedAlarms() {
+ setNetworkConnected(true);
+ mDeviceIdleController.setJobsActive(false);
+ mDeviceIdleController.setAlarmsActive(false);
+ mDeviceIdleController.setActiveIdleOpsForTest(0);
+
+ final ArgumentCaptor alarmListenerCaptor = ArgumentCaptor
+ .forClass(AlarmManager.OnAlarmListener.class);
+ doNothing().when(mAlarmManager).setWindow(anyInt(), anyLong(), anyLong(),
+ eq("DeviceIdleController.light"), alarmListenerCaptor.capture(), any());
+ doNothing().when(mAlarmManager).setWindow(anyInt(), anyLong(), anyLong(),
+ eq("DeviceIdleController.light"), alarmListenerCaptor.capture(), any());
+
+ // Set state to INACTIVE.
+ mDeviceIdleController.becomeActiveLocked("testing", 0);
+ setChargingOn(false);
+ setScreenOn(false);
+ verifyLightStateConditions(LIGHT_STATE_INACTIVE);
+
+ final AlarmManager.OnAlarmListener progressionListener =
+ alarmListenerCaptor.getAllValues().get(0);
+ final AlarmManager.OnAlarmListener maintenanceListener =
+ alarmListenerCaptor.getAllValues().get(1);
+
+ // Set things to make it look like the INACTIVE -> IDLE alarm didn't fire and the
+ // MAINTENANCE alarm just fired.
+ mInjector.nowElapsed = mDeviceIdleController.getNextLightMaintenanceAlarmTimeForTesting();
+ // If the non-wakeup alarm doesn't fire in a timely manner, we would see both fire at the
+ // same time.
+ progressionListener.onAlarm();
+ verifyLightStateConditions(LIGHT_STATE_IDLE_MAINTENANCE);
+ maintenanceListener.onAlarm();
+ verifyLightStateConditions(LIGHT_STATE_IDLE_MAINTENANCE);
+
+ assertTrue(mInjector.nowElapsed < mDeviceIdleController.getNextLightAlarmTimeForTesting());
+
+ // MAINTENANCE->IDLE alarm goes off at correct time.
+ mInjector.nowElapsed = mDeviceIdleController.getNextLightAlarmTimeForTesting();
+ progressionListener.onAlarm();
+ verifyLightStateConditions(LIGHT_STATE_IDLE);
+
+ // Go back to MAINTENANCE
+ mInjector.nowElapsed = mDeviceIdleController.getNextLightMaintenanceAlarmTimeForTesting();
+ maintenanceListener.onAlarm();
+ verifyLightStateConditions(LIGHT_STATE_IDLE_MAINTENANCE);
+
+ assertTrue(mInjector.nowElapsed < mDeviceIdleController.getNextLightAlarmTimeForTesting());
+ assertTrue(mInjector.nowElapsed
+ < mDeviceIdleController.getNextLightMaintenanceAlarmTimeForTesting());
+
+ // MAINTENANCE->IDLE alarm is delayed until IDLE->MAINTENANCE alarm goes off.
+ mInjector.nowElapsed = mDeviceIdleController.getNextLightMaintenanceAlarmTimeForTesting();
+ progressionListener.onAlarm();
+ verifyLightStateConditions(LIGHT_STATE_IDLE_MAINTENANCE);
+ maintenanceListener.onAlarm();
+ verifyLightStateConditions(LIGHT_STATE_IDLE_MAINTENANCE);
+ }
+
+ @Test
+ public void testLightStepIdleStateIdlingTimeIncreases() {
+ final long maintenanceTimeMs = 60_000L;
+ mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET = maintenanceTimeMs;
+ mConstants.LIGHT_IDLE_MAINTENANCE_MAX_BUDGET = maintenanceTimeMs;
+ mConstants.LIGHT_IDLE_TIMEOUT = 5 * 60_000L;
+ mConstants.LIGHT_MAX_IDLE_TIMEOUT = 20 * 60_000L;
+ mConstants.LIGHT_IDLE_FACTOR = 2f;
+
+ setNetworkConnected(true);
+ mDeviceIdleController.setJobsActive(false);
+ mDeviceIdleController.setAlarmsActive(false);
+ mDeviceIdleController.setActiveIdleOpsForTest(0);
+
+ InOrder alarmManagerInOrder = inOrder(mAlarmManager);
+
+ final ArgumentCaptor alarmListenerCaptor = ArgumentCaptor
+ .forClass(AlarmManager.OnAlarmListener.class);
+ doNothing().when(mAlarmManager).setWindow(anyInt(), anyLong(), anyLong(),
+ eq("DeviceIdleController.light"), alarmListenerCaptor.capture(), any());
+ doNothing().when(mAlarmManager).setWindow(anyInt(), anyLong(), anyLong(),
+ eq("DeviceIdleController.light"), alarmListenerCaptor.capture(), any());
+
+ // Set state to INACTIVE.
+ mDeviceIdleController.becomeActiveLocked("testing", 0);
+ setChargingOn(false);
+ setScreenOn(false);
+ verifyLightStateConditions(LIGHT_STATE_INACTIVE);
+ long idlingTimeMs = mConstants.LIGHT_IDLE_TIMEOUT;
+ final long idleAfterInactiveExpiryTime =
+ mInjector.nowElapsed + mConstants.LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT;
+ alarmManagerInOrder.verify(mAlarmManager).setWindow(
+ eq(AlarmManager.ELAPSED_REALTIME),
+ eq(idleAfterInactiveExpiryTime),
+ anyLong(), anyString(), any(), any());
+ // Maintenance alarm
+ alarmManagerInOrder.verify(mAlarmManager).setWindow(
+ eq(AlarmManager.ELAPSED_REALTIME_WAKEUP),
+ eq(idleAfterInactiveExpiryTime + idlingTimeMs),
+ anyLong(), anyString(), any(), any());
+
+ final AlarmManager.OnAlarmListener progressionListener =
+ alarmListenerCaptor.getAllValues().get(0);
+ final AlarmManager.OnAlarmListener maintenanceListener =
+ alarmListenerCaptor.getAllValues().get(1);
+
+ // INACTIVE -> IDLE alarm
+ mInjector.nowElapsed = mDeviceIdleController.getNextLightAlarmTimeForTesting();
+ progressionListener.onAlarm();
+ verifyLightStateConditions(LIGHT_STATE_IDLE);
+ alarmManagerInOrder.verify(mAlarmManager).setWindow(
+ eq(AlarmManager.ELAPSED_REALTIME_WAKEUP),
+ eq(mInjector.nowElapsed + idlingTimeMs),
+ anyLong(), anyString(), any(), any());
+
+ for (int i = 0; i < 2; ++i) {
+ // IDLE->MAINTENANCE alarm
+ mInjector.nowElapsed =
+ mDeviceIdleController.getNextLightMaintenanceAlarmTimeForTesting();
+ maintenanceListener.onAlarm();
+ verifyLightStateConditions(LIGHT_STATE_IDLE_MAINTENANCE);
+ long maintenanceExpiryTime = mInjector.nowElapsed + maintenanceTimeMs;
+ idlingTimeMs *= mConstants.LIGHT_IDLE_FACTOR;
+ // Set MAINTENANCE->IDLE
+ alarmManagerInOrder.verify(mAlarmManager).setWindow(
+ eq(AlarmManager.ELAPSED_REALTIME),
+ eq(maintenanceExpiryTime),
+ anyLong(), anyString(), any(), any());
+ // Set IDLE->MAINTENANCE
+ alarmManagerInOrder.verify(mAlarmManager).setWindow(
+ eq(AlarmManager.ELAPSED_REALTIME_WAKEUP),
+ eq(maintenanceExpiryTime + idlingTimeMs),
+ anyLong(), anyString(), any(), any());
+
+ // MAINTENANCE->IDLE alarm
+ mInjector.nowElapsed = mDeviceIdleController.getNextLightAlarmTimeForTesting();
+ progressionListener.onAlarm();
+ verifyLightStateConditions(LIGHT_STATE_IDLE);
+ // Set IDLE->MAINTENANCE again
+ alarmManagerInOrder.verify(mAlarmManager).setWindow(
+ eq(AlarmManager.ELAPSED_REALTIME_WAKEUP),
+ eq(mInjector.nowElapsed + idlingTimeMs),
+ anyLong(), anyString(), any(), any());
+ }
+ }
+
@Test
public void testLightIdleAlarmUnaffectedByMotion() {
setNetworkConnected(true);
@@ -1043,45 +1180,37 @@ public class DeviceIdleControllerTest {
verifyLightStateConditions(LIGHT_STATE_INACTIVE);
// No active ops means INACTIVE should go straight to IDLE.
- mDeviceIdleController.stepLightIdleStateLocked("testing");
+ mDeviceIdleController.stepLightIdleStateLocked("testing", true);
verifyLightStateConditions(LIGHT_STATE_IDLE);
- inOrder.verify(mDeviceIdleController).scheduleLightAlarmLocked(
- longThat(l -> l == mConstants.LIGHT_IDLE_TIMEOUT),
- longThat(l -> l == mConstants.LIGHT_IDLE_TIMEOUT_INITIAL_FLEX),
- eq(false));
+ inOrder.verify(mDeviceIdleController).scheduleLightMaintenanceAlarmLocked(
+ longThat(l -> l == mConstants.LIGHT_IDLE_TIMEOUT));
// Should just alternate between IDLE and IDLE_MAINTENANCE now.
- mDeviceIdleController.stepLightIdleStateLocked("testing");
+ mDeviceIdleController.stepLightIdleStateLocked("testing", true);
verifyLightStateConditions(LIGHT_STATE_IDLE_MAINTENANCE);
inOrder.verify(mDeviceIdleController).scheduleLightAlarmLocked(
longThat(l -> l >= mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET),
- longThat(l -> l == mConstants.FLEX_TIME_SHORT),
- eq(true));
+ longThat(l -> l == mConstants.FLEX_TIME_SHORT));
- mDeviceIdleController.stepLightIdleStateLocked("testing");
+ mDeviceIdleController.stepLightIdleStateLocked("testing", true);
verifyLightStateConditions(LIGHT_STATE_IDLE);
- inOrder.verify(mDeviceIdleController).scheduleLightAlarmLocked(
- longThat(l -> l > mConstants.LIGHT_IDLE_TIMEOUT),
- longThat(l -> l > mConstants.LIGHT_IDLE_TIMEOUT_INITIAL_FLEX),
- eq(false));
+ inOrder.verify(mDeviceIdleController).scheduleLightMaintenanceAlarmLocked(
+ longThat(l -> l > mConstants.LIGHT_IDLE_TIMEOUT));
- mDeviceIdleController.stepLightIdleStateLocked("testing");
+ mDeviceIdleController.stepLightIdleStateLocked("testing", true);
verifyLightStateConditions(LIGHT_STATE_IDLE_MAINTENANCE);
inOrder.verify(mDeviceIdleController).scheduleLightAlarmLocked(
longThat(l -> l >= mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET),
- longThat(l -> l == mConstants.FLEX_TIME_SHORT),
- eq(true));
+ longThat(l -> l == mConstants.FLEX_TIME_SHORT));
// Test that motion doesn't reset the idle timeout.
mDeviceIdleController.handleMotionDetectedLocked(50, "test");
- mDeviceIdleController.stepLightIdleStateLocked("testing");
+ mDeviceIdleController.stepLightIdleStateLocked("testing", true);
verifyLightStateConditions(LIGHT_STATE_IDLE);
- inOrder.verify(mDeviceIdleController).scheduleLightAlarmLocked(
- longThat(l -> l > mConstants.LIGHT_IDLE_TIMEOUT),
- longThat(l -> l > mConstants.LIGHT_IDLE_TIMEOUT_INITIAL_FLEX),
- eq(false));
+ inOrder.verify(mDeviceIdleController).scheduleLightMaintenanceAlarmLocked(
+ longThat(l -> l > mConstants.LIGHT_IDLE_TIMEOUT));
}
///////////////// EXIT conditions ///////////////////
@@ -1268,10 +1397,6 @@ public class DeviceIdleControllerTest {
mDeviceIdleController.exitMaintenanceEarlyIfNeededLocked();
verifyLightStateConditions(LIGHT_STATE_INACTIVE);
- enterLightState(LIGHT_STATE_PRE_IDLE);
- mDeviceIdleController.exitMaintenanceEarlyIfNeededLocked();
- verifyLightStateConditions(LIGHT_STATE_IDLE);
-
enterLightState(LIGHT_STATE_IDLE);
mDeviceIdleController.exitMaintenanceEarlyIfNeededLocked();
verifyLightStateConditions(LIGHT_STATE_IDLE);
@@ -1307,10 +1432,6 @@ public class DeviceIdleControllerTest {
mDeviceIdleController.exitMaintenanceEarlyIfNeededLocked();
verifyLightStateConditions(LIGHT_STATE_INACTIVE);
- enterLightState(LIGHT_STATE_PRE_IDLE);
- mDeviceIdleController.exitMaintenanceEarlyIfNeededLocked();
- verifyLightStateConditions(LIGHT_STATE_PRE_IDLE);
-
enterLightState(LIGHT_STATE_IDLE);
mDeviceIdleController.exitMaintenanceEarlyIfNeededLocked();
verifyLightStateConditions(LIGHT_STATE_IDLE);
@@ -1344,10 +1465,6 @@ public class DeviceIdleControllerTest {
mDeviceIdleController.exitMaintenanceEarlyIfNeededLocked();
verifyLightStateConditions(LIGHT_STATE_INACTIVE);
- enterLightState(LIGHT_STATE_PRE_IDLE);
- mDeviceIdleController.exitMaintenanceEarlyIfNeededLocked();
- verifyLightStateConditions(LIGHT_STATE_PRE_IDLE);
-
enterLightState(LIGHT_STATE_IDLE);
mDeviceIdleController.exitMaintenanceEarlyIfNeededLocked();
verifyLightStateConditions(LIGHT_STATE_IDLE);
@@ -1381,10 +1498,6 @@ public class DeviceIdleControllerTest {
mDeviceIdleController.exitMaintenanceEarlyIfNeededLocked();
verifyLightStateConditions(LIGHT_STATE_INACTIVE);
- enterLightState(LIGHT_STATE_PRE_IDLE);
- mDeviceIdleController.exitMaintenanceEarlyIfNeededLocked();
- verifyLightStateConditions(LIGHT_STATE_PRE_IDLE);
-
enterLightState(LIGHT_STATE_IDLE);
mDeviceIdleController.exitMaintenanceEarlyIfNeededLocked();
verifyLightStateConditions(LIGHT_STATE_IDLE);
@@ -1510,10 +1623,6 @@ public class DeviceIdleControllerTest {
mDeviceIdleController.handleMotionDetectedLocked(50, "test");
verifyLightStateConditions(LIGHT_STATE_INACTIVE);
- enterLightState(LIGHT_STATE_PRE_IDLE);
- mDeviceIdleController.handleMotionDetectedLocked(50, "test");
- verifyLightStateConditions(LIGHT_STATE_PRE_IDLE);
-
enterLightState(LIGHT_STATE_IDLE);
mDeviceIdleController.handleMotionDetectedLocked(50, "test");
verifyLightStateConditions(LIGHT_STATE_IDLE);
@@ -1580,10 +1689,6 @@ public class DeviceIdleControllerTest {
mDeviceIdleController.becomeActiveLocked("test", 1000);
verifyLightStateConditions(LIGHT_STATE_ACTIVE);
- enterLightState(LIGHT_STATE_PRE_IDLE);
- mDeviceIdleController.becomeActiveLocked("test", 1000);
- verifyLightStateConditions(LIGHT_STATE_ACTIVE);
-
enterLightState(LIGHT_STATE_IDLE);
mDeviceIdleController.becomeActiveLocked("test", 1000);
verifyLightStateConditions(LIGHT_STATE_ACTIVE);
@@ -2059,7 +2164,7 @@ public class DeviceIdleControllerTest {
while (mDeviceIdleController.getLightState() != lightState) {
// Stepping through each state ensures that the proper features are turned
// on/off.
- mDeviceIdleController.stepLightIdleStateLocked("testing");
+ mDeviceIdleController.stepLightIdleStateLocked("testing", true);
count++;
if (count > 10) {
@@ -2068,7 +2173,6 @@ public class DeviceIdleControllerTest {
}
}
break;
- case LIGHT_STATE_PRE_IDLE:
case LIGHT_STATE_WAITING_FOR_NETWORK:
case LIGHT_STATE_OVERRIDE:
setScreenOn(false);
@@ -2213,7 +2317,6 @@ public class DeviceIdleControllerTest {
> mAlarmManager.getNextWakeFromIdleTime());
break;
case LIGHT_STATE_INACTIVE:
- case LIGHT_STATE_PRE_IDLE:
case LIGHT_STATE_IDLE:
case LIGHT_STATE_WAITING_FOR_NETWORK:
case LIGHT_STATE_IDLE_MAINTENANCE: