Make Light Doze timings deterministic.

Switch the light idle timing mechanism to ensure the system always exits
light idle at consistent times after screen off. With this change, we
don't need to formally enter light idle to get into the maintenance
window at the correct time, so we change that alarm to a non-wakeup
alarm. The alarm will fire when the CPU is awake. If the CPU isn't
awake, then waking up the CPU just to say we're going to light idle
doesn't provide much benefit. We keep a wakeup alarm only for coming out
of light idle to enter the maintenance window.

Also, removing the PRE_IDLE state and combining the duration with the
INACTIVE->IDLE time.

Bug: 208820086
Test: atest DeviceIdleTest
Test: atest FrameworksMockingServicesTests:DeviceIdleControllerTest
Change-Id: Icccf78850039c51ac67abc5fa126c6a33f51dfba
This commit is contained in:
Kweku Adams
2022-01-10 23:45:34 +00:00
parent e7e3dd676c
commit 1256b08bb3
2 changed files with 398 additions and 277 deletions

View File

@@ -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;
<pre>
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);

View File

@@ -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<AlarmManager.OnAlarmListener> 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<AlarmManager.OnAlarmListener> 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: