diff options
| author | 2015-10-15 12:38:14 -0700 | |
|---|---|---|
| committer | 2015-10-20 10:43:14 -0700 | |
| commit | 08c47a5dece977a55d250d98bda9e2a8df8b6ed0 (patch) | |
| tree | 3d4222f8027e5cacd63884057ddac93cc910e3b9 | |
| parent | 96a0cb21bab2a0ed967457a30d39b7e4329c5f7f (diff) | |
Initial implementation of light-weight idle mode.
This mode turns on after the screen has been off for 15 minutes,
and then cycles through 15 minutes of idle and 1 minute of
maintenance, ragardless of whether the device is moving around.
It currently only impacts network access and sync/job scheduling.
It does not remove access to wake locks or alarms for any apps.
It also doesn't report in the public API that the device is in
idle mode (since it isn't modifying the behavior of the power
manager) -- this is probably what we desire, since we don't want
stuff like GCM to be reporting these frequent changes.
We'll probably at least want to have the alarm manager do some
kind of more aggressive batching of alarms in this most (not allowing
more than one wakeup every minute?). That's for the future.
Also updated batterystats to include this new information, which
means the format of some of the data has changed -- device_idle
is no longer a flag, but an enum of (off, light, full), and there
is no information about time spent in light modes.
Also added new data about the maximum duration spent in both light
and full idle modes, to get a better understanding of how those
are behaving.
And did a little cleanup of DeviceIdleController, removing the
sensing alarm which was redundant with the regular alarm.
Change-Id: Ibeea6659577dc02deff58f048f97fcd9b0223307
12 files changed, 547 insertions, 129 deletions
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index 8e86a53072e7..1aa5c66202f2 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -169,7 +169,7 @@ public abstract class BatteryStats implements Parcelable { /** * Current version of checkin data format. */ - static final String CHECKIN_VERSION = "15"; + static final String CHECKIN_VERSION = "16"; /** * Old version, we hit 9 and ran out of room, need to remove. @@ -468,8 +468,8 @@ public abstract class BatteryStats implements Parcelable { * @param cluster the index of the CPU cluster. * @param step the index of the CPU speed. This is not the actual speed of the CPU. * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT. - * @see PowerProfile.getNumCpuClusters() - * @see PowerProfile.getNumSpeedStepsInCpuCluster(int) + * @see com.android.internal.os.PowerProfile#getNumCpuClusters() + * @see com.android.internal.os.PowerProfile#getNumSpeedStepsInCpuCluster(int) */ public abstract long getTimeAtCpuSpeed(int cluster, int step, int which); @@ -1135,14 +1135,15 @@ public abstract class BatteryStats implements Parcelable { public static final int STATE2_WIFI_RUNNING_FLAG = 1<<29; public static final int STATE2_WIFI_ON_FLAG = 1<<28; public static final int STATE2_FLASHLIGHT_FLAG = 1<<27; - public static final int STATE2_DEVICE_IDLE_FLAG = 1<<26; - public static final int STATE2_CHARGING_FLAG = 1<<25; - public static final int STATE2_PHONE_IN_CALL_FLAG = 1<<24; - public static final int STATE2_BLUETOOTH_ON_FLAG = 1<<23; - public static final int STATE2_CAMERA_FLAG = 1<<22; + public static final int STATE2_DEVICE_IDLE_SHIFT = 25; + public static final int STATE2_DEVICE_IDLE_MASK = 0x3 << STATE2_DEVICE_IDLE_SHIFT; + public static final int STATE2_CHARGING_FLAG = 1<<24; + public static final int STATE2_PHONE_IN_CALL_FLAG = 1<<23; + public static final int STATE2_BLUETOOTH_ON_FLAG = 1<<22; + public static final int STATE2_CAMERA_FLAG = 1<<21; public static final int MOST_INTERESTING_STATES2 = - STATE2_POWER_SAVE_FLAG | STATE2_WIFI_ON_FLAG | STATE2_DEVICE_IDLE_FLAG + STATE2_POWER_SAVE_FLAG | STATE2_WIFI_ON_FLAG | STATE2_DEVICE_IDLE_MASK | STATE2_CHARGING_FLAG | STATE2_PHONE_IN_CALL_FLAG | STATE2_BLUETOOTH_ON_FLAG; public static final int SETTLE_TO_ZERO_STATES2 = 0xffff0000 & ~MOST_INTERESTING_STATES2; @@ -1620,36 +1621,57 @@ public abstract class BatteryStats implements Parcelable { public abstract int getPowerSaveModeEnabledCount(int which); /** + * Constant for device idle mode: not active. + */ + public static final int DEVICE_IDLE_MODE_OFF = 0; + + /** + * Constant for device idle mode: active in lightweight mode. + */ + public static final int DEVICE_IDLE_MODE_LIGHT = 1; + + /** + * Constant for device idle mode: active in full mode. + */ + public static final int DEVICE_IDLE_MODE_FULL = 2; + + /** * Returns the time in microseconds that device has been in idle mode while * running on battery. * * {@hide} */ - public abstract long getDeviceIdleModeEnabledTime(long elapsedRealtimeUs, int which); + public abstract long getDeviceIdleModeTime(int mode, long elapsedRealtimeUs, int which); /** * Returns the number of times that the devie has gone in to idle mode. * * {@hide} */ - public abstract int getDeviceIdleModeEnabledCount(int which); + public abstract int getDeviceIdleModeCount(int mode, int which); + + /** + * Return the longest duration we spent in a particular device idle mode (fully in the + * mode, not in idle maintenance etc). + */ + public abstract long getLongestDeviceIdleModeTime(int mode); /** * Returns the time in microseconds that device has been in idling while on - * battery. This is broader than {@link #getDeviceIdleModeEnabledTime} -- it + * battery. This is broader than {@link #getDeviceIdleModeTime} -- it * counts all of the time that we consider the device to be idle, whether or not * it is currently in the actual device idle mode. * * {@hide} */ - public abstract long getDeviceIdlingTime(long elapsedRealtimeUs, int which); + public abstract long getDeviceIdlingTime(int mode, long elapsedRealtimeUs, int which); /** * Returns the number of times that the devie has started idling. * * {@hide} */ - public abstract int getDeviceIdlingCount(int which); + public abstract int getDeviceIdlingCount(int mode, int which); /** * Returns the number of times that connectivity state changed. @@ -1847,7 +1869,10 @@ public abstract class BatteryStats implements Parcelable { new BitDescription(HistoryItem.STATE2_WIFI_RUNNING_FLAG, "wifi_running", "Ww"), new BitDescription(HistoryItem.STATE2_WIFI_ON_FLAG, "wifi", "W"), new BitDescription(HistoryItem.STATE2_FLASHLIGHT_FLAG, "flashlight", "fl"), - new BitDescription(HistoryItem.STATE2_DEVICE_IDLE_FLAG, "device_idle", "di"), + new BitDescription(HistoryItem.STATE2_DEVICE_IDLE_MASK, + HistoryItem.STATE2_DEVICE_IDLE_SHIFT, "device_idle", "di", + new String[] { "off", "light", "full", "???" }, + new String[] { "off", "light", "full", "???" }), new BitDescription(HistoryItem.STATE2_CHARGING_FLAG, "charging", "ch"), new BitDescription(HistoryItem.STATE2_PHONE_IN_CALL_FLAG, "phone_in_call", "Pcl"), new BitDescription(HistoryItem.STATE2_BLUETOOTH_ON_FLAG, "bluetooth", "b"), @@ -2529,8 +2554,14 @@ public abstract class BatteryStats implements Parcelable { final long screenOnTime = getScreenOnTime(rawRealtime, which); final long interactiveTime = getInteractiveTime(rawRealtime, which); final long powerSaveModeEnabledTime = getPowerSaveModeEnabledTime(rawRealtime, which); - final long deviceIdleModeEnabledTime = getDeviceIdleModeEnabledTime(rawRealtime, which); - final long deviceIdlingTime = getDeviceIdlingTime(rawRealtime, which); + final long deviceIdleModeLightTime = getDeviceIdleModeTime(DEVICE_IDLE_MODE_LIGHT, + rawRealtime, which); + final long deviceIdleModeFullTime = getDeviceIdleModeTime(DEVICE_IDLE_MODE_FULL, + rawRealtime, which); + final long deviceLightIdlingTime = getDeviceIdlingTime(DEVICE_IDLE_MODE_LIGHT, + rawRealtime, which); + final long deviceIdlingTime = getDeviceIdlingTime(DEVICE_IDLE_MODE_FULL, + rawRealtime, which); final int connChanges = getNumConnectivityChange(which); final long phoneOnTime = getPhoneOnTime(rawRealtime, which); @@ -2613,11 +2644,15 @@ public abstract class BatteryStats implements Parcelable { fullWakeLockTimeTotal / 1000, partialWakeLockTimeTotal / 1000, getMobileRadioActiveTime(rawRealtime, which) / 1000, getMobileRadioActiveAdjustedTime(which) / 1000, interactiveTime / 1000, - powerSaveModeEnabledTime / 1000, connChanges, deviceIdleModeEnabledTime / 1000, - getDeviceIdleModeEnabledCount(which), deviceIdlingTime / 1000, - getDeviceIdlingCount(which), + powerSaveModeEnabledTime / 1000, connChanges, deviceIdleModeFullTime / 1000, + getDeviceIdleModeCount(DEVICE_IDLE_MODE_FULL, which), deviceIdlingTime / 1000, + getDeviceIdlingCount(DEVICE_IDLE_MODE_FULL, which), getMobileRadioActiveCount(which), - getMobileRadioActiveUnknownTime(which) / 1000); + getMobileRadioActiveUnknownTime(which) / 1000, deviceIdleModeLightTime / 1000, + getDeviceIdleModeCount(DEVICE_IDLE_MODE_LIGHT, which), deviceLightIdlingTime / 1000, + getDeviceIdlingCount(DEVICE_IDLE_MODE_LIGHT, which), + getLongestDeviceIdleModeTime(DEVICE_IDLE_MODE_LIGHT), + getLongestDeviceIdleModeTime(DEVICE_IDLE_MODE_FULL)); // Dump screen brightness stats Object[] args = new Object[NUM_SCREEN_BRIGHTNESS_BINS]; @@ -3082,8 +3117,14 @@ public abstract class BatteryStats implements Parcelable { final long screenOnTime = getScreenOnTime(rawRealtime, which); final long interactiveTime = getInteractiveTime(rawRealtime, which); final long powerSaveModeEnabledTime = getPowerSaveModeEnabledTime(rawRealtime, which); - final long deviceIdleModeEnabledTime = getDeviceIdleModeEnabledTime(rawRealtime, which); - final long deviceIdlingTime = getDeviceIdlingTime(rawRealtime, which); + final long deviceIdleModeLightTime = getDeviceIdleModeTime(DEVICE_IDLE_MODE_LIGHT, + rawRealtime, which); + final long deviceIdleModeFullTime = getDeviceIdleModeTime(DEVICE_IDLE_MODE_FULL, + rawRealtime, which); + final long deviceLightIdlingTime = getDeviceIdlingTime(DEVICE_IDLE_MODE_LIGHT, + rawRealtime, which); + final long deviceIdlingTime = getDeviceIdlingTime(DEVICE_IDLE_MODE_FULL, + rawRealtime, which); final long phoneOnTime = getPhoneOnTime(rawRealtime, which); final long wifiRunningTime = getGlobalWifiRunningTime(rawRealtime, which); final long wifiOnTime = getWifiOnTime(rawRealtime, which); @@ -3127,26 +3168,54 @@ public abstract class BatteryStats implements Parcelable { sb.append(")"); pw.println(sb.toString()); } + if (deviceLightIdlingTime != 0) { + sb.setLength(0); + sb.append(prefix); + sb.append(" Device light idling: "); + formatTimeMs(sb, deviceLightIdlingTime / 1000); + sb.append("("); + sb.append(formatRatioLocked(deviceLightIdlingTime, whichBatteryRealtime)); + sb.append(") "); sb.append(getDeviceIdlingCount(DEVICE_IDLE_MODE_LIGHT, which)); + sb.append("x"); + pw.println(sb.toString()); + } + if (deviceIdleModeLightTime != 0) { + sb.setLength(0); + sb.append(prefix); + sb.append(" Idle mode light time: "); + formatTimeMs(sb, deviceIdleModeLightTime / 1000); + sb.append("("); + sb.append(formatRatioLocked(deviceIdleModeLightTime, whichBatteryRealtime)); + sb.append(") "); + sb.append(getDeviceIdleModeCount(DEVICE_IDLE_MODE_LIGHT, which)); + sb.append("x"); + sb.append(" -- longest "); + formatTimeMs(sb, getLongestDeviceIdleModeTime(DEVICE_IDLE_MODE_LIGHT)); + pw.println(sb.toString()); + } if (deviceIdlingTime != 0) { sb.setLength(0); sb.append(prefix); - sb.append(" Device idling: "); + sb.append(" Device full idling: "); formatTimeMs(sb, deviceIdlingTime / 1000); sb.append("("); sb.append(formatRatioLocked(deviceIdlingTime, whichBatteryRealtime)); - sb.append(") "); sb.append(getDeviceIdlingCount(which)); + sb.append(") "); sb.append(getDeviceIdlingCount(DEVICE_IDLE_MODE_FULL, which)); sb.append("x"); pw.println(sb.toString()); } - if (deviceIdleModeEnabledTime != 0) { + if (deviceIdleModeFullTime != 0) { sb.setLength(0); sb.append(prefix); - sb.append(" Idle mode time: "); - formatTimeMs(sb, deviceIdleModeEnabledTime / 1000); + sb.append(" Idle mode full time: "); + formatTimeMs(sb, deviceIdleModeFullTime / 1000); sb.append("("); - sb.append(formatRatioLocked(deviceIdleModeEnabledTime, whichBatteryRealtime)); - sb.append(") "); sb.append(getDeviceIdleModeEnabledCount(which)); + sb.append(formatRatioLocked(deviceIdleModeFullTime, whichBatteryRealtime)); + sb.append(") "); + sb.append(getDeviceIdleModeCount(DEVICE_IDLE_MODE_FULL, which)); sb.append("x"); + sb.append(" -- longest "); + formatTimeMs(sb, getLongestDeviceIdleModeTime(DEVICE_IDLE_MODE_FULL)); pw.println(sb.toString()); } if (phoneOnTime != 0) { diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl index 9fdbec39b2bd..dd10df33fe60 100644 --- a/core/java/android/os/IPowerManager.aidl +++ b/core/java/android/os/IPowerManager.aidl @@ -47,6 +47,7 @@ interface IPowerManager boolean isPowerSaveMode(); boolean setPowerSaveMode(boolean mode); boolean isDeviceIdleMode(); + boolean isLightDeviceIdleMode(); void reboot(boolean confirm, String reason, boolean wait); void shutdown(boolean confirm, String reason, boolean wait); diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index 69974fa9d965..1cffa8321dfb 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -910,6 +910,26 @@ public final class PowerManager { } /** + * Returns true if the device is currently in light idle mode. This happens when a device + * has had its screen off for a short time, switching it into a batching mode where we + * execute jobs, syncs, networking on a batching schedule. You can monitor for changes to + * this state with {@link #ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED}. + * + * @return Returns true if currently in active light device idle mode, else false. This is + * when light idle mode restrictions are being actively applied; it will return false if the + * device is in a long-term idle mode but currently running a maintenance window where + * restrictions have been lifted. + * @hide + */ + public boolean isLightDeviceIdleMode() { + try { + return mService.isLightDeviceIdleMode(); + } catch (RemoteException e) { + return false; + } + } + + /** * Return whether the given application package name is on the device's power whitelist. * Apps can be placed on the whitelist through the settings UI invoked by * {@link android.provider.Settings#ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS}. @@ -961,6 +981,15 @@ public final class PowerManager { = "android.os.action.DEVICE_IDLE_MODE_CHANGED"; /** + * Intent that is broadcast when the state of {@link #isLightDeviceIdleMode()} changes. + * This broadcast is only sent to registered receivers. + * @hide + */ + @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED + = "android.os.action.LIGHT_DEVICE_IDLE_MODE_CHANGED"; + + /** * @hide Intent that is broadcast when the set of power save whitelist apps has changed. * This broadcast is only sent to registered receivers. */ diff --git a/core/java/android/os/PowerManagerInternal.java b/core/java/android/os/PowerManagerInternal.java index b6d0fcb448f5..9801e1bef2c2 100644 --- a/core/java/android/os/PowerManagerInternal.java +++ b/core/java/android/os/PowerManagerInternal.java @@ -148,7 +148,9 @@ public abstract class PowerManagerInternal { public void onLowPowerModeChanged(boolean enabled); } - public abstract void setDeviceIdleMode(boolean enabled); + public abstract boolean setDeviceIdleMode(boolean enabled); + + public abstract boolean setLightDeviceIdleMode(boolean enabled); public abstract void setDeviceIdleWhitelist(int[] appids); diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl index 3cddbf6b6a56..3b9b8dbd4c71 100644 --- a/core/java/com/android/internal/app/IBatteryStats.aidl +++ b/core/java/com/android/internal/app/IBatteryStats.aidl @@ -117,7 +117,7 @@ interface IBatteryStats { void noteWifiRadioPowerState(int powerState, long timestampNs); void noteNetworkInterfaceType(String iface, int type); void noteNetworkStatsEnabled(); - void noteDeviceIdleMode(boolean enabled, String activeReason, int activeUid); + void noteDeviceIdleMode(int mode, String activeReason, int activeUid); void setBatteryState(int status, int health, int plugType, int level, int temp, int volt); long getAwakeTimeBattery(); long getAwakeTimePlugged(); diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 64b7768c8337..f73df005c7f1 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -105,7 +105,7 @@ public final class BatteryStatsImpl extends BatteryStats { private static final int MAGIC = 0xBA757475; // 'BATSTATS' // Current on-disk Parcel version - private static final int VERSION = 132 + (USE_OLD_HISTORY ? 1000 : 0); + private static final int VERSION = 135 + (USE_OLD_HISTORY ? 1000 : 0); // Maximum number of items we will record in the history. private static final int MAX_HISTORY_ITEMS = 2000; @@ -338,8 +338,15 @@ public final class BatteryStatsImpl extends BatteryStats { boolean mDeviceIdling; StopwatchTimer mDeviceIdlingTimer; - boolean mDeviceIdleModeEnabled; - StopwatchTimer mDeviceIdleModeEnabledTimer; + boolean mDeviceLightIdling; + StopwatchTimer mDeviceLightIdlingTimer; + + int mDeviceIdleMode; + long mLastIdleTimeStart; + long mLongestLightIdleTime; + long mLongestFullIdleTime; + StopwatchTimer mDeviceIdleModeLightTimer; + StopwatchTimer mDeviceIdleModeFullTimer; boolean mPhoneOn; StopwatchTimer mPhoneOnTimer; @@ -3219,42 +3226,69 @@ public final class BatteryStatsImpl extends BatteryStats { } } - public void noteDeviceIdleModeLocked(boolean enabled, String activeReason, int activeUid) { + public void noteDeviceIdleModeLocked(int mode, String activeReason, int activeUid) { final long elapsedRealtime = SystemClock.elapsedRealtime(); final long uptime = SystemClock.uptimeMillis(); - boolean nowIdling = enabled; - if (mDeviceIdling && !enabled && activeReason == null) { + boolean nowIdling = mode == DEVICE_IDLE_MODE_FULL; + if (mDeviceIdling && !nowIdling && activeReason == null) { // We don't go out of general idling mode until explicitly taken out of // device idle through going active or significant motion. nowIdling = true; } + boolean nowLightIdling = mode == DEVICE_IDLE_MODE_LIGHT; + if (mDeviceLightIdling && !nowLightIdling && !nowIdling && activeReason == null) { + // We don't go out of general light idling mode until explicitly taken out of + // device idle through going active or significant motion. + nowLightIdling = true; + } + if (activeReason != null && (mDeviceIdling || mDeviceLightIdling)) { + addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_ACTIVE, + activeReason, activeUid); + } if (mDeviceIdling != nowIdling) { mDeviceIdling = nowIdling; int stepState = nowIdling ? STEP_LEVEL_MODE_DEVICE_IDLE : 0; mModStepMode |= (mCurStepMode&STEP_LEVEL_MODE_DEVICE_IDLE) ^ stepState; mCurStepMode = (mCurStepMode&~STEP_LEVEL_MODE_DEVICE_IDLE) | stepState; - if (enabled) { + if (nowIdling) { mDeviceIdlingTimer.startRunningLocked(elapsedRealtime); } else { mDeviceIdlingTimer.stopRunningLocked(elapsedRealtime); } } - if (mDeviceIdleModeEnabled != enabled) { - mDeviceIdleModeEnabled = enabled; - addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_ACTIVE, - activeReason != null ? activeReason : "", activeUid); - if (enabled) { - mHistoryCur.states2 |= HistoryItem.STATE2_DEVICE_IDLE_FLAG; - if (DEBUG_HISTORY) Slog.v(TAG, "Device idle mode enabled to: " - + Integer.toHexString(mHistoryCur.states2)); - mDeviceIdleModeEnabledTimer.startRunningLocked(elapsedRealtime); + if (mDeviceLightIdling != nowLightIdling) { + mDeviceLightIdling = nowLightIdling; + if (nowLightIdling) { + mDeviceLightIdlingTimer.startRunningLocked(elapsedRealtime); } else { - mHistoryCur.states2 &= ~HistoryItem.STATE2_DEVICE_IDLE_FLAG; - if (DEBUG_HISTORY) Slog.v(TAG, "Device idle mode disabled to: " - + Integer.toHexString(mHistoryCur.states2)); - mDeviceIdleModeEnabledTimer.stopRunningLocked(elapsedRealtime); + mDeviceLightIdlingTimer.stopRunningLocked(elapsedRealtime); } + } + if (mDeviceIdleMode != mode) { + mHistoryCur.states2 = (mHistoryCur.states2 & ~HistoryItem.STATE2_DEVICE_IDLE_MASK) + | (mode << HistoryItem.STATE2_DEVICE_IDLE_SHIFT); + if (DEBUG_HISTORY) Slog.v(TAG, "Device idle mode changed to: " + + Integer.toHexString(mHistoryCur.states2)); addHistoryRecordLocked(elapsedRealtime, uptime); + long lastDuration = elapsedRealtime - mLastIdleTimeStart; + mLastIdleTimeStart = elapsedRealtime; + if (mDeviceIdleMode == DEVICE_IDLE_MODE_LIGHT) { + if (lastDuration > mLongestLightIdleTime) { + mLongestLightIdleTime = lastDuration; + } + mDeviceIdleModeLightTimer.stopRunningLocked(elapsedRealtime); + } else if (mDeviceIdleMode == DEVICE_IDLE_MODE_FULL) { + if (lastDuration > mLongestFullIdleTime) { + mLongestFullIdleTime = lastDuration; + } + mDeviceIdleModeFullTimer.stopRunningLocked(elapsedRealtime); + } + if (mode == DEVICE_IDLE_MODE_LIGHT) { + mDeviceIdleModeLightTimer.startRunningLocked(elapsedRealtime); + } else if (mode == DEVICE_IDLE_MODE_FULL) { + mDeviceIdleModeFullTimer.startRunningLocked(elapsedRealtime); + } + mDeviceIdleMode = mode; } } @@ -4140,20 +4174,55 @@ public final class BatteryStatsImpl extends BatteryStats { return mPowerSaveModeEnabledTimer.getCountLocked(which); } - @Override public long getDeviceIdleModeEnabledTime(long elapsedRealtimeUs, int which) { - return mDeviceIdleModeEnabledTimer.getTotalTimeLocked(elapsedRealtimeUs, which); + @Override public long getDeviceIdleModeTime(int mode, long elapsedRealtimeUs, + int which) { + switch (mode) { + case DEVICE_IDLE_MODE_LIGHT: + return mDeviceIdleModeLightTimer.getTotalTimeLocked(elapsedRealtimeUs, which); + case DEVICE_IDLE_MODE_FULL: + return mDeviceIdleModeFullTimer.getTotalTimeLocked(elapsedRealtimeUs, which); + } + return 0; + } + + @Override public int getDeviceIdleModeCount(int mode, int which) { + switch (mode) { + case DEVICE_IDLE_MODE_LIGHT: + return mDeviceIdleModeLightTimer.getCountLocked(which); + case DEVICE_IDLE_MODE_FULL: + return mDeviceIdleModeFullTimer.getCountLocked(which); + } + return 0; } - @Override public int getDeviceIdleModeEnabledCount(int which) { - return mDeviceIdleModeEnabledTimer.getCountLocked(which); + @Override public long getLongestDeviceIdleModeTime(int mode) { + switch (mode) { + case DEVICE_IDLE_MODE_LIGHT: + return mLongestLightIdleTime; + case DEVICE_IDLE_MODE_FULL: + return mLongestFullIdleTime; + } + return 0; } - @Override public long getDeviceIdlingTime(long elapsedRealtimeUs, int which) { - return mDeviceIdlingTimer.getTotalTimeLocked(elapsedRealtimeUs, which); + @Override public long getDeviceIdlingTime(int mode, long elapsedRealtimeUs, int which) { + switch (mode) { + case DEVICE_IDLE_MODE_LIGHT: + return mDeviceLightIdlingTimer.getTotalTimeLocked(elapsedRealtimeUs, which); + case DEVICE_IDLE_MODE_FULL: + return mDeviceIdlingTimer.getTotalTimeLocked(elapsedRealtimeUs, which); + } + return 0; } - @Override public int getDeviceIdlingCount(int which) { - return mDeviceIdlingTimer.getCountLocked(which); + @Override public int getDeviceIdlingCount(int mode, int which) { + switch (mode) { + case DEVICE_IDLE_MODE_LIGHT: + return mDeviceLightIdlingTimer.getCountLocked(which); + case DEVICE_IDLE_MODE_FULL: + return mDeviceIdlingTimer.getCountLocked(which); + } + return 0; } @Override public int getNumConnectivityChange(int which) { @@ -6855,7 +6924,9 @@ public final class BatteryStatsImpl extends BatteryStats { } mInteractiveTimer = new StopwatchTimer(null, -10, null, mOnBatteryTimeBase); mPowerSaveModeEnabledTimer = new StopwatchTimer(null, -2, null, mOnBatteryTimeBase); - mDeviceIdleModeEnabledTimer = new StopwatchTimer(null, -11, null, mOnBatteryTimeBase); + mDeviceIdleModeLightTimer = new StopwatchTimer(null, -11, null, mOnBatteryTimeBase); + mDeviceIdleModeFullTimer = new StopwatchTimer(null, -14, null, mOnBatteryTimeBase); + mDeviceLightIdlingTimer = new StopwatchTimer(null, -15, null, mOnBatteryTimeBase); mDeviceIdlingTimer = new StopwatchTimer(null, -12, null, mOnBatteryTimeBase); mPhoneOnTimer = new StopwatchTimer(null, -3, null, mOnBatteryTimeBase); for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) { @@ -7484,7 +7555,11 @@ public final class BatteryStatsImpl extends BatteryStats { } mInteractiveTimer.reset(false); mPowerSaveModeEnabledTimer.reset(false); - mDeviceIdleModeEnabledTimer.reset(false); + mLongestLightIdleTime = 0; + mLongestFullIdleTime = 0; + mDeviceIdleModeLightTimer.reset(false); + mDeviceIdleModeFullTimer.reset(false); + mDeviceLightIdlingTimer.reset(false); mDeviceIdlingTimer.reset(false); mPhoneOnTimer.reset(false); mAudioOnTimer.reset(false); @@ -9224,7 +9299,11 @@ public final class BatteryStatsImpl extends BatteryStats { mInteractiveTimer.readSummaryFromParcelLocked(in); mPhoneOn = false; mPowerSaveModeEnabledTimer.readSummaryFromParcelLocked(in); - mDeviceIdleModeEnabledTimer.readSummaryFromParcelLocked(in); + mLongestLightIdleTime = in.readLong(); + mLongestFullIdleTime = in.readLong(); + mDeviceIdleModeLightTimer.readSummaryFromParcelLocked(in); + mDeviceIdleModeFullTimer.readSummaryFromParcelLocked(in); + mDeviceLightIdlingTimer.readSummaryFromParcelLocked(in); mDeviceIdlingTimer.readSummaryFromParcelLocked(in); mPhoneOnTimer.readSummaryFromParcelLocked(in); for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) { @@ -9558,7 +9637,11 @@ public final class BatteryStatsImpl extends BatteryStats { } mInteractiveTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS); mPowerSaveModeEnabledTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS); - mDeviceIdleModeEnabledTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS); + out.writeLong(mLongestLightIdleTime); + out.writeLong(mLongestFullIdleTime); + mDeviceIdleModeLightTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS); + mDeviceIdleModeFullTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS); + mDeviceLightIdlingTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS); mDeviceIdlingTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS); mPhoneOnTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS); for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) { @@ -9892,7 +9975,11 @@ public final class BatteryStatsImpl extends BatteryStats { mInteractiveTimer = new StopwatchTimer(null, -10, null, mOnBatteryTimeBase, in); mPhoneOn = false; mPowerSaveModeEnabledTimer = new StopwatchTimer(null, -2, null, mOnBatteryTimeBase, in); - mDeviceIdleModeEnabledTimer = new StopwatchTimer(null, -11, null, mOnBatteryTimeBase, in); + mLongestLightIdleTime = in.readLong(); + mLongestFullIdleTime = in.readLong(); + mDeviceIdleModeLightTimer = new StopwatchTimer(null, -14, null, mOnBatteryTimeBase, in); + mDeviceIdleModeFullTimer = new StopwatchTimer(null, -11, null, mOnBatteryTimeBase, in); + mDeviceLightIdlingTimer = new StopwatchTimer(null, -15, null, mOnBatteryTimeBase, in); mDeviceIdlingTimer = new StopwatchTimer(null, -12, null, mOnBatteryTimeBase, in); mPhoneOnTimer = new StopwatchTimer(null, -3, null, mOnBatteryTimeBase, in); for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) { @@ -10053,7 +10140,11 @@ public final class BatteryStatsImpl extends BatteryStats { } mInteractiveTimer.writeToParcel(out, uSecRealtime); mPowerSaveModeEnabledTimer.writeToParcel(out, uSecRealtime); - mDeviceIdleModeEnabledTimer.writeToParcel(out, uSecRealtime); + out.writeLong(mLongestLightIdleTime); + out.writeLong(mLongestFullIdleTime); + mDeviceIdleModeLightTimer.writeToParcel(out, uSecRealtime); + mDeviceIdleModeFullTimer.writeToParcel(out, uSecRealtime); + mDeviceLightIdlingTimer.writeToParcel(out, uSecRealtime); mDeviceIdlingTimer.writeToParcel(out, uSecRealtime); mPhoneOnTimer.writeToParcel(out, uSecRealtime); for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) { @@ -10188,8 +10279,12 @@ public final class BatteryStatsImpl extends BatteryStats { mInteractiveTimer.logState(pr, " "); pr.println("*** Power save mode timer:"); mPowerSaveModeEnabledTimer.logState(pr, " "); - pr.println("*** Device idle mode timer:"); - mDeviceIdleModeEnabledTimer.logState(pr, " "); + pr.println("*** Device idle mode light timer:"); + mDeviceIdleModeLightTimer.logState(pr, " "); + pr.println("*** Device idle mode full timer:"); + mDeviceIdleModeFullTimer.logState(pr, " "); + pr.println("*** Device light idling timer:"); + mDeviceLightIdlingTimer.logState(pr, " "); pr.println("*** Device idling timer:"); mDeviceIdlingTimer.logState(pr, " "); pr.println("*** Phone timer:"); diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java index 87b490d7ea25..496e4dfda6e0 100644 --- a/services/core/java/com/android/server/DeviceIdleController.java +++ b/services/core/java/com/android/server/DeviceIdleController.java @@ -109,6 +109,9 @@ public class DeviceIdleController extends SystemService private static final String ACTION_STEP_IDLE_STATE = "com.android.server.device_idle.STEP_IDLE_STATE"; + private static final String ACTION_STEP_LIGHT_IDLE_STATE = + "com.android.server.device_idle.STEP_LIGHT_IDLE_STATE"; + private AlarmManager mAlarmManager; private IBatteryStats mBatteryStats; private PowerManagerInternal mLocalPowerManager; @@ -118,16 +121,16 @@ public class DeviceIdleController extends SystemService private Sensor mMotionSensor; private LocationManager mLocationManager; private LocationRequest mLocationRequest; - private PendingIntent mSensingAlarmIntent; private PendingIntent mAlarmIntent; + private PendingIntent mLightAlarmIntent; private Intent mIdleIntent; + private Intent mLightIdleIntent; private Display mCurDisplay; private AnyMotionDetector mAnyMotionDetector; private boolean mEnabled; private boolean mForceIdle; private boolean mScreenOn; private boolean mCharging; - private boolean mSensing; private boolean mNotMoving; private boolean mLocating; private boolean mLocated; @@ -138,7 +141,7 @@ public class DeviceIdleController extends SystemService /** Device is currently active. */ private static final int STATE_ACTIVE = 0; - /** Device is inactve (screen off, no motion) and we are waiting to for idle. */ + /** Device is inactive (screen off, no motion) and we are waiting to for idle. */ private static final int STATE_INACTIVE = 1; /** Device is past the initial inactive period, and waiting for the next idle period. */ private static final int STATE_IDLE_PENDING = 2; @@ -163,12 +166,35 @@ public class DeviceIdleController extends SystemService } } + /** Device is currently active. */ + private static final int LIGHT_STATE_ACTIVE = 0; + /** Device is inactive (screen off) and we are waiting to for the first light idle. */ + private static final int LIGHT_STATE_INACTIVE = 1; + /** Device is in the light idle state, trying to stay asleep as much as possible. */ + private static final int LIGHT_STATE_IDLE = 2; + /** Device is in the light idle state, but temporarily out of idle to do regular maintenance. */ + private static final int LIGHT_STATE_IDLE_MAINTENANCE = 3; + /** Device light idle state is overriden, now applying full doze state. */ + private static final int LIGHT_STATE_OVERRIDE = 4; + private static String lightStateToString(int state) { + switch (state) { + case LIGHT_STATE_ACTIVE: return "ACTIVE"; + case LIGHT_STATE_INACTIVE: return "INACTIVE"; + case LIGHT_STATE_IDLE: return "IDLE"; + case LIGHT_STATE_IDLE_MAINTENANCE: return "IDLE_MAINTENANCE"; + case LIGHT_STATE_OVERRIDE: return "OVERRIDE"; + default: return Integer.toString(state); + } + } + private int mState; + private int mLightState; private long mInactiveTimeout; private long mNextAlarmTime; private long mNextIdlePendingDelay; private long mNextIdleDelay; + private long mNextLightAlarmTime; public final AtomicFile mConfigFile; @@ -250,10 +276,14 @@ public class DeviceIdleController extends SystemService if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { Uri data = intent.getData(); String ssp; - if (data != null && (ssp=data.getSchemeSpecificPart()) != null) { + if (data != null && (ssp = data.getSchemeSpecificPart()) != null) { removePowerSaveWhitelistAppInternal(ssp); } } + } else if (ACTION_STEP_LIGHT_IDLE_STATE.equals(intent.getAction())) { + synchronized (DeviceIdleController.this) { + stepLightIdleStateLocked(); + } } else if (ACTION_STEP_IDLE_STATE.equals(intent.getAction())) { synchronized (DeviceIdleController.this) { stepIdleStateLocked(); @@ -305,7 +335,7 @@ public class DeviceIdleController extends SystemService public void onAccuracyChanged(Sensor sensor, int accuracy) {} public boolean registerLocked() { - boolean success = false; + boolean success; if (mMotionSensor.getReportingMode() == Sensor.REPORTING_MODE_ONE_SHOT) { success = mSensorManager.requestTriggerSensor(mMotionListener, mMotionSensor); } else { @@ -380,6 +410,8 @@ public class DeviceIdleController extends SystemService */ private final class Constants extends ContentObserver { // Key names stored in the settings value. + private static final String KEY_LIGHT_IDLE_TIMEOUT = "light_idle_to"; + private static final String KEY_LIGHT_IDLE_PENDING_TIMEOUT = "light_idle_pending_to"; private static final String KEY_INACTIVE_TIMEOUT = "inactive_to"; private static final String KEY_SENSING_TIMEOUT = "sensing_to"; private static final String KEY_LOCATING_TIMEOUT = "locating_to"; @@ -401,6 +433,22 @@ public class DeviceIdleController extends SystemService "sms_temp_app_whitelist_duration"; /** + * This is the time, after becoming inactive, that we will start going + * in to light-weight idle mode. + * @see Settings.Global#DEVICE_IDLE_CONSTANTS + * @see #KEY_LIGHT_IDLE_TIMEOUT + */ + public long LIGHT_IDLE_TIMEOUT; + + /** + * This is the initial time, after light idle idle, that we will will sit in the + * LIGHT_IDLE_MAINTENANCE period for the system to run normally before returning to idle. + * @see Settings.Global#DEVICE_IDLE_CONSTANTS + * @see #KEY_LIGHT_IDLE_PENDING_TIMEOUT + */ + public long LIGHT_IDLE_PENDING_TIMEOUT; + + /** * This is the time, after becoming inactive, at which we start looking at the * motion sensor to determine if the device is being left alone. We don't do this * immediately after going inactive just because we don't want to be continually running @@ -455,7 +503,8 @@ public class DeviceIdleController extends SystemService /** * This is the initial time, after being idle, that we will allow ourself to be back - * in the IDLE_PENDING state allowing the system to run normally until we return to idle. + * in the IDLE_MAINTENANCE state allowing the system to run normally until we return to + * idle. * @see Settings.Global#DEVICE_IDLE_CONSTANTS * @see #KEY_IDLE_PENDING_TIMEOUT */ @@ -555,6 +604,10 @@ public class DeviceIdleController extends SystemService Slog.e(TAG, "Bad device idle settings", e); } + LIGHT_IDLE_TIMEOUT = mParser.getLong(KEY_LIGHT_IDLE_TIMEOUT, + !COMPRESS_TIME ? 15 * 60 * 1000L : 60 * 1000L); + LIGHT_IDLE_PENDING_TIMEOUT = mParser.getLong(KEY_LIGHT_IDLE_PENDING_TIMEOUT, + !COMPRESS_TIME ? 1 * 60 * 1000L : 15 * 1000L); INACTIVE_TIMEOUT = mParser.getLong(KEY_INACTIVE_TIMEOUT, !COMPRESS_TIME ? 30 * 60 * 1000L : 3 * 60 * 1000L); SENSING_TIMEOUT = mParser.getLong(KEY_SENSING_TIMEOUT, @@ -592,6 +645,14 @@ public class DeviceIdleController extends SystemService void dump(PrintWriter pw) { pw.println(" Settings:"); + 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_PENDING_TIMEOUT); pw.print("="); + TimeUtils.formatDuration(LIGHT_IDLE_PENDING_TIMEOUT, pw); + pw.println(); + pw.print(" "); pw.print(KEY_INACTIVE_TIMEOUT); pw.print("="); TimeUtils.formatDuration(INACTIVE_TIMEOUT, pw); pw.println(); @@ -689,9 +750,10 @@ public class DeviceIdleController extends SystemService static final int MSG_WRITE_CONFIG = 1; static final int MSG_REPORT_IDLE_ON = 2; - static final int MSG_REPORT_IDLE_OFF = 3; - static final int MSG_REPORT_ACTIVE = 4; - static final int MSG_TEMP_APP_WHITELIST_TIMEOUT = 5; + static final int MSG_REPORT_IDLE_ON_LIGHT = 3; + static final int MSG_REPORT_IDLE_OFF = 4; + static final int MSG_REPORT_ACTIVE = 5; + static final int MSG_TEMP_APP_WHITELIST_TIMEOUT = 6; final class MyHandler extends Handler { MyHandler(Looper looper) { @@ -704,43 +766,70 @@ public class DeviceIdleController extends SystemService case MSG_WRITE_CONFIG: { handleWriteConfigFile(); } break; - case MSG_REPORT_IDLE_ON: { + case MSG_REPORT_IDLE_ON: + case MSG_REPORT_IDLE_ON_LIGHT: { EventLogTags.writeDeviceIdleOnStart(); - mLocalPowerManager.setDeviceIdleMode(true); + final boolean fullChanged; + final boolean lightChanged; + if (msg.what == MSG_REPORT_IDLE_ON) { + fullChanged = mLocalPowerManager.setDeviceIdleMode(true); + lightChanged = mLocalPowerManager.setLightDeviceIdleMode(false); + } else { + fullChanged = mLocalPowerManager.setDeviceIdleMode(false); + lightChanged = mLocalPowerManager.setLightDeviceIdleMode(true); + } try { mNetworkPolicyManager.setDeviceIdleMode(true); - mBatteryStats.noteDeviceIdleMode(true, null, Process.myUid()); + mBatteryStats.noteDeviceIdleMode(msg.what == MSG_REPORT_IDLE_ON + ? BatteryStats.DEVICE_IDLE_MODE_FULL + : BatteryStats.DEVICE_IDLE_MODE_LIGHT, null, Process.myUid()); } catch (RemoteException e) { } - getContext().sendBroadcastAsUser(mIdleIntent, UserHandle.ALL); + if (fullChanged) { + getContext().sendBroadcastAsUser(mIdleIntent, UserHandle.ALL); + } + if (lightChanged) { + getContext().sendBroadcastAsUser(mLightIdleIntent, UserHandle.ALL); + } EventLogTags.writeDeviceIdleOnComplete(); } break; case MSG_REPORT_IDLE_OFF: { EventLogTags.writeDeviceIdleOffStart("unknown"); - mLocalPowerManager.setDeviceIdleMode(false); + final boolean fullChanged = mLocalPowerManager.setDeviceIdleMode(false); + final boolean lightChanged = mLocalPowerManager.setLightDeviceIdleMode(false); try { mNetworkPolicyManager.setDeviceIdleMode(false); - mBatteryStats.noteDeviceIdleMode(false, null, Process.myUid()); + mBatteryStats.noteDeviceIdleMode(BatteryStats.DEVICE_IDLE_MODE_OFF, + null, Process.myUid()); } catch (RemoteException e) { } - getContext().sendBroadcastAsUser(mIdleIntent, UserHandle.ALL); + if (fullChanged) { + getContext().sendBroadcastAsUser(mIdleIntent, UserHandle.ALL); + } + if (lightChanged) { + getContext().sendBroadcastAsUser(mLightIdleIntent, UserHandle.ALL); + } EventLogTags.writeDeviceIdleOffComplete(); } break; case MSG_REPORT_ACTIVE: { String activeReason = (String)msg.obj; int activeUid = msg.arg1; - boolean needBroadcast = msg.arg2 != 0; EventLogTags.writeDeviceIdleOffStart( activeReason != null ? activeReason : "unknown"); - mLocalPowerManager.setDeviceIdleMode(false); + final boolean fullChanged = mLocalPowerManager.setDeviceIdleMode(false); + final boolean lightChanged = mLocalPowerManager.setLightDeviceIdleMode(false); try { mNetworkPolicyManager.setDeviceIdleMode(false); - mBatteryStats.noteDeviceIdleMode(false, activeReason, activeUid); + mBatteryStats.noteDeviceIdleMode(BatteryStats.DEVICE_IDLE_MODE_OFF, + activeReason, activeUid); } catch (RemoteException e) { } - if (needBroadcast) { + if (fullChanged) { getContext().sendBroadcastAsUser(mIdleIntent, UserHandle.ALL); } + if (lightChanged) { + getContext().sendBroadcastAsUser(mLightIdleIntent, UserHandle.ALL); + } EventLogTags.writeDeviceIdleOffComplete(); } break; case MSG_TEMP_APP_WHITELIST_TIMEOUT: { @@ -909,6 +998,7 @@ public class DeviceIdleController extends SystemService // a battery update the next time the level drops. mCharging = true; mState = STATE_ACTIVE; + mLightState = LIGHT_STATE_ACTIVE; mInactiveTimeout = mConstants.INACTIVE_TIMEOUT; } @@ -967,18 +1057,22 @@ public class DeviceIdleController extends SystemService .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); mAlarmIntent = PendingIntent.getBroadcast(getContext(), 0, intent, 0); - Intent intentSensing = new Intent(ACTION_STEP_IDLE_STATE) + Intent intentLight = new Intent(ACTION_STEP_LIGHT_IDLE_STATE) .setPackage("android") .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); - mSensingAlarmIntent = PendingIntent.getBroadcast(getContext(), 0, intentSensing, 0); + mLightAlarmIntent = PendingIntent.getBroadcast(getContext(), 0, intentLight, 0); mIdleIntent = new Intent(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED); mIdleIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND); + mLightIdleIntent = new Intent(PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED); + mLightIdleIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY + | Intent.FLAG_RECEIVER_FOREGROUND); IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_BATTERY_CHANGED); filter.addAction(ACTION_STEP_IDLE_STATE); + filter.addAction(ACTION_STEP_LIGHT_IDLE_STATE); getContext().registerReceiver(mReceiver, filter); filter = new IntentFilter(); filter.addAction(Intent.ACTION_PACKAGE_REMOVED); @@ -1283,32 +1377,43 @@ public class DeviceIdleController extends SystemService } void scheduleReportActiveLocked(String activeReason, int activeUid) { - Message msg = mHandler.obtainMessage(MSG_REPORT_ACTIVE, activeUid, - mState == STATE_IDLE ? 1 : 0, activeReason); + Message msg = mHandler.obtainMessage(MSG_REPORT_ACTIVE, activeUid, 0, activeReason); mHandler.sendMessage(msg); } void becomeActiveLocked(String activeReason, int activeUid) { if (DEBUG) Slog.i(TAG, "becomeActiveLocked, reason = " + activeReason); - if (mState != STATE_ACTIVE) { + if (mState != STATE_ACTIVE || mLightState != STATE_ACTIVE) { EventLogTags.writeDeviceIdle(STATE_ACTIVE, activeReason); + EventLogTags.writeDeviceIdleLight(LIGHT_STATE_ACTIVE, activeReason); scheduleReportActiveLocked(activeReason, activeUid); mState = STATE_ACTIVE; + mLightState = LIGHT_STATE_ACTIVE; mInactiveTimeout = mConstants.INACTIVE_TIMEOUT; resetIdleManagementLocked(); + resetLightIdleManagementLocked(); } } void becomeInactiveIfAppropriateLocked() { if (DEBUG) Slog.d(TAG, "becomeInactiveIfAppropriateLocked()"); - if (((!mScreenOn && !mCharging) || mForceIdle) && mEnabled && mState == STATE_ACTIVE) { + if (((!mScreenOn && !mCharging) || mForceIdle) && mEnabled) { // Screen has turned off; we are now going to become inactive and start // waiting to see if we will ultimately go idle. - mState = STATE_INACTIVE; - if (DEBUG) Slog.d(TAG, "Moved from STATE_ACTIVE to STATE_INACTIVE"); - resetIdleManagementLocked(); - scheduleAlarmLocked(mInactiveTimeout, false); - EventLogTags.writeDeviceIdle(mState, "no activity"); + if (mState == STATE_ACTIVE) { + mState = STATE_INACTIVE; + if (DEBUG) Slog.d(TAG, "Moved from STATE_ACTIVE to STATE_INACTIVE"); + resetIdleManagementLocked(); + scheduleAlarmLocked(mInactiveTimeout, false); + EventLogTags.writeDeviceIdle(mState, "no activity"); + } + if (mLightState == LIGHT_STATE_ACTIVE) { + mLightState = LIGHT_STATE_INACTIVE; + if (DEBUG) Slog.d(TAG, "Moved from LIGHT_STATE_ACTIVE to LIGHT_STATE_INACTIVE"); + resetLightIdleManagementLocked(); + scheduleLightAlarmLocked(mConstants.LIGHT_IDLE_TIMEOUT); + EventLogTags.writeDeviceIdleLight(mLightState, "no activity"); + } } } @@ -1316,12 +1421,15 @@ public class DeviceIdleController extends SystemService mNextIdlePendingDelay = 0; mNextIdleDelay = 0; cancelAlarmLocked(); - cancelSensingAlarmLocked(); cancelLocatingLocked(); stopMonitoringMotionLocked(); mAnyMotionDetector.stop(); } + void resetLightIdleManagementLocked() { + cancelLightAlarmLocked(); + } + void exitForceIdleLocked() { if (mForceIdle) { mForceIdle = false; @@ -1331,6 +1439,37 @@ public class DeviceIdleController extends SystemService } } + void stepLightIdleStateLocked() { + if (mLightState == LIGHT_STATE_OVERRIDE) { + // If we are already in full device idle mode, then + // there is nothing left to do for light mode. + return; + } + + if (DEBUG) Slog.d(TAG, "stepLightIdleStateLocked: mLightState=" + mLightState); + EventLogTags.writeDeviceIdleLightStep(); + + switch (mLightState) { + case LIGHT_STATE_INACTIVE: + case LIGHT_STATE_IDLE_MAINTENANCE: + scheduleLightAlarmLocked(mConstants.LIGHT_IDLE_TIMEOUT); + if (DEBUG) Slog.d(TAG, "Moved to LIGHT_STATE_IDLE."); + mLightState = LIGHT_STATE_IDLE; + EventLogTags.writeDeviceIdleLight(mLightState, "step"); + mHandler.sendEmptyMessage(MSG_REPORT_IDLE_ON_LIGHT); + break; + case LIGHT_STATE_IDLE: + // We have been idling long enough, now it is time to do some work. + scheduleLightAlarmLocked(mConstants.LIGHT_IDLE_PENDING_TIMEOUT); + if (DEBUG) Slog.d(TAG, + "Moved from LIGHT_STATE_IDLE to LIGHT_STATE_IDLE_MAINTENANCE."); + mLightState = LIGHT_STATE_IDLE_MAINTENANCE; + EventLogTags.writeDeviceIdleLight(mLightState, "step"); + mHandler.sendEmptyMessage(MSG_REPORT_IDLE_OFF); + break; + } + } + void stepIdleStateLocked() { if (DEBUG) Slog.d(TAG, "stepIdleStateLocked: mState=" + mState); EventLogTags.writeDeviceIdleStep(); @@ -1361,8 +1500,7 @@ public class DeviceIdleController extends SystemService mState = STATE_SENSING; if (DEBUG) Slog.d(TAG, "Moved from STATE_IDLE_PENDING to STATE_SENSING."); EventLogTags.writeDeviceIdle(mState, "step"); - scheduleSensingAlarmLocked(mConstants.SENSING_TIMEOUT); - cancelSensingAlarmLocked(); + cancelAlarmLocked(); cancelLocatingLocked(); mAnyMotionDetector.checkForAnyMotion(); mNotMoving = false; @@ -1374,8 +1512,7 @@ public class DeviceIdleController extends SystemService mState = STATE_LOCATING; if (DEBUG) Slog.d(TAG, "Moved from STATE_SENSING to STATE_LOCATING."); EventLogTags.writeDeviceIdle(mState, "step"); - cancelSensingAlarmLocked(); - scheduleSensingAlarmLocked(mConstants.LOCATING_TIMEOUT); + scheduleAlarmLocked(mConstants.LOCATING_TIMEOUT, false); if (mLocationManager != null && mLocationManager.getProvider(LocationManager.NETWORK_PROVIDER) != null) { mLocationManager.requestLocationUpdates(mLocationRequest, @@ -1401,7 +1538,7 @@ public class DeviceIdleController extends SystemService // Otherwise, we have to move from locating into idle maintenance. case STATE_LOCATING: - cancelSensingAlarmLocked(); + cancelAlarmLocked(); cancelLocatingLocked(); mAnyMotionDetector.stop(); case STATE_IDLE_MAINTENANCE: @@ -1412,6 +1549,10 @@ public class DeviceIdleController extends SystemService if (DEBUG) Slog.d(TAG, "Setting mNextIdleDelay = " + mNextIdleDelay); mNextIdleDelay = Math.min(mNextIdleDelay, mConstants.MAX_IDLE_TIMEOUT); mState = STATE_IDLE; + if (mLightState != LIGHT_STATE_OVERRIDE) { + mLightState = LIGHT_STATE_OVERRIDE; + cancelLightAlarmLocked(); + } EventLogTags.writeDeviceIdle(mState, "step"); mHandler.sendEmptyMessage(MSG_REPORT_IDLE_ON); break; @@ -1439,11 +1580,22 @@ public class DeviceIdleController extends SystemService // The device is not yet active, so we want to go back to the pending idle // state to wait again for no motion. Note that we only monitor for motion // after moving out of the inactive state, so no need to worry about that. + boolean becomeInactive = false; if (mState != STATE_ACTIVE) { scheduleReportActiveLocked(type, Process.myUid()); mState = STATE_ACTIVE; mInactiveTimeout = timeout; EventLogTags.writeDeviceIdle(mState, type); + becomeInactive = true; + } + if (mLightState == LIGHT_STATE_OVERRIDE) { + // We went out of light idle mode because we had started full idle mode... let's + // now go back and reset things so we resume light idling if appropriate. + mLightState = STATE_ACTIVE; + EventLogTags.writeDeviceIdleLight(mLightState, type); + becomeInactive = true; + } + if (becomeInactive) { becomeInactiveIfAppropriateLocked(); } } @@ -1501,11 +1653,10 @@ public class DeviceIdleController extends SystemService } } - void cancelSensingAlarmLocked() { - if (mSensing) { - if (DEBUG) Slog.d(TAG, "cancelSensingAlarmLocked()"); - mAlarmManager.cancel(mSensingAlarmIntent); - mSensing = false; + void cancelLightAlarmLocked() { + if (mNextLightAlarmTime != 0) { + mNextLightAlarmTime = 0; + mAlarmManager.cancel(mLightAlarmIntent); } } @@ -1536,13 +1687,18 @@ public class DeviceIdleController extends SystemService } } - void scheduleSensingAlarmLocked(long delay) { - if (DEBUG) Slog.d(TAG, "scheduleSensingAlarmLocked(" + delay + ")"); - cancelSensingAlarmLocked(); - mNextAlarmTime = SystemClock.elapsedRealtime() + delay; + void scheduleLightAlarmLocked(long delay) { + if (DEBUG) Slog.d(TAG, "scheduleLightAlarmLocked(" + delay + ")"); + if (mMotionSensor == null) { + // If there is no motion sensor on this device, then we won't schedule + // alarms, because we can't determine if the device is not moving. This effectively + // turns off normal execution of device idling, although it is still possible to + // manually poke it by pretending like the alarm is going off. + return; + } + mNextLightAlarmTime = SystemClock.elapsedRealtime() + delay; mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, - mNextAlarmTime, mSensingAlarmIntent); - mSensing = true; + mNextLightAlarmTime, mLightAlarmIntent); } private static int[] buildAppIdArray(ArrayMap<String, Integer> systemApps, @@ -1778,7 +1934,21 @@ public class DeviceIdleController extends SystemService try { exitForceIdleLocked(); stepIdleStateLocked(); - pw.print("Stepped to: "); pw.println(stateToString(mState)); + pw.print("Stepped to: "); + pw.println(stateToString(mState)); + } finally { + Binder.restoreCallingIdentity(token); + } + } + } else if ("light-step".equals(cmd)) { + getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, + null); + synchronized (this) { + long token = Binder.clearCallingIdentity(); + try { + exitForceIdleLocked(); + stepLightIdleStateLocked(); + pw.print("Stepped to: "); pw.println(lightStateToString(mLightState)); } finally { Binder.restoreCallingIdentity(token); } @@ -2041,8 +2211,7 @@ public class DeviceIdleController extends SystemService pw.print(" mScreenOn="); pw.println(mScreenOn); pw.print(" mCharging="); pw.println(mCharging); pw.print(" mMotionActive="); pw.println(mMotionListener.active); - pw.print(" mSensing="); pw.print(mSensing); pw.print(" mNotMoving="); - pw.println(mNotMoving); + pw.print(" mNotMoving="); pw.println(mNotMoving); pw.print(" mLocating="); pw.print(mLocating); pw.print(" mHasGps="); pw.print(mHasGps); pw.print(" mHasNetwork="); pw.print(mHasNetworkLocation); pw.print(" mLocated="); pw.println(mLocated); @@ -2052,7 +2221,9 @@ public class DeviceIdleController extends SystemService if (mLastGpsLocation != null) { pw.print(" mLastGpsLocation="); pw.println(mLastGpsLocation); } - pw.print(" mState="); pw.println(stateToString(mState)); + pw.print(" mState="); pw.print(stateToString(mState)); + pw.print(" mLightState="); + pw.println(lightStateToString(mLightState)); pw.print(" mInactiveTimeout="); TimeUtils.formatDuration(mInactiveTimeout, pw); pw.println(); if (mNextAlarmTime != 0) { @@ -2070,6 +2241,11 @@ public class DeviceIdleController extends SystemService TimeUtils.formatDuration(mNextIdleDelay, pw); pw.println(); } + if (mNextLightAlarmTime != 0) { + pw.print(" mNextLightAlarmTime="); + TimeUtils.formatDuration(mNextLightAlarmTime, SystemClock.elapsedRealtime(), pw); + pw.println(); + } } } } diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags index 9bf2aaad597e..516e2f462a59 100644 --- a/services/core/java/com/android/server/EventLogTags.logtags +++ b/services/core/java/com/android/server/EventLogTags.logtags @@ -194,6 +194,8 @@ option java_package com.android.server 34006 device_idle_off_start (reason|3) 34007 device_idle_off_phase (what|3) 34008 device_idle_off_complete +34009 device_idle_light (state|1|5), (reason|3) +34010 device_idle_light_step # --------------------------- # DisplayManagerService.java diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index 62768c3b6475..c7228ce47683 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -834,10 +834,10 @@ public final class BatteryStatsService extends IBatteryStats.Stub } @Override - public void noteDeviceIdleMode(boolean enabled, String activeReason, int activeUid) { + public void noteDeviceIdleMode(int mode, String activeReason, int activeUid) { enforceCallingPermission(); synchronized (mStats) { - mStats.noteDeviceIdleModeLocked(enabled, activeReason, activeUid); + mStats.noteDeviceIdleModeLocked(mode, activeReason, activeUid); } } diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java index e6dc8959f219..0a22308bbec4 100644 --- a/services/core/java/com/android/server/content/SyncManager.java +++ b/services/core/java/com/android/server/content/SyncManager.java @@ -258,7 +258,8 @@ public class SyncManager { private BroadcastReceiver mDeviceIdleReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - boolean idle = mPowerManager.isDeviceIdleMode(); + boolean idle = mPowerManager.isDeviceIdleMode() + || mPowerManager.isLightDeviceIdleMode(); mDeviceIsIdle = idle; if (idle) { cancelActiveSync( @@ -478,6 +479,7 @@ public class SyncManager { context.registerReceiver(mStorageIntentReceiver, intentFilter); intentFilter = new IntentFilter(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED); + intentFilter.addAction(PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED); context.registerReceiver(mDeviceIdleReceiver, intentFilter); intentFilter = new IntentFilter(Intent.ACTION_SHUTDOWN); diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java index 06bd583fc7b3..0e57a01884fb 100644 --- a/services/core/java/com/android/server/job/JobSchedulerService.java +++ b/services/core/java/com/android/server/job/JobSchedulerService.java @@ -162,8 +162,12 @@ public class JobSchedulerService extends com.android.server.SystemService Slog.d(TAG, "Removing jobs for user: " + userId); } cancelJobsForUser(userId); - } else if (PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED.equals(intent.getAction())) { - updateIdleMode(mPowerManager != null ? mPowerManager.isDeviceIdleMode() : false); + } else if (PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED.equals(intent.getAction()) + || PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED.equals(intent.getAction())) { + updateIdleMode(mPowerManager != null + ? (mPowerManager.isDeviceIdleMode() + || mPowerManager.isLightDeviceIdleMode()) + : false); } } }; @@ -340,6 +344,7 @@ public class JobSchedulerService extends com.android.server.SystemService mBroadcastReceiver, UserHandle.ALL, filter, null, null); final IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_REMOVED); userFilter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED); + userFilter.addAction(PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED); getContext().registerReceiverAsUser( mBroadcastReceiver, UserHandle.ALL, userFilter, null, null); mPowerManager = (PowerManager)getContext().getSystemService(Context.POWER_SERVICE); diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index ced04336dfac..6498dd941a32 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -444,6 +444,9 @@ public final class PowerManagerService extends SystemService // True if we are currently in device idle mode. private boolean mDeviceIdleMode; + // True if we are currently in light device idle mode. + private boolean mLightDeviceIdleMode; + // Set of app ids that we will always respect the wake locks for. int[] mDeviceIdleWhitelist = new int[0]; @@ -2292,12 +2295,18 @@ public final class PowerManagerService extends SystemService } } - private boolean isDeviceIdleModeInternal() { + boolean isDeviceIdleModeInternal() { synchronized (mLock) { return mDeviceIdleMode; } } + boolean isLightDeviceIdleModeInternal() { + synchronized (mLock) { + return mLightDeviceIdleMode; + } + } + private void handleBatteryStateChangedLocked() { mDirty |= DIRTY_BATTERY_STATE; updatePowerStateLocked(); @@ -2368,7 +2377,7 @@ public final class PowerManagerService extends SystemService } } - void setDeviceIdleModeInternal(boolean enabled) { + boolean setDeviceIdleModeInternal(boolean enabled) { synchronized (mLock) { if (mDeviceIdleMode != enabled) { mDeviceIdleMode = enabled; @@ -2378,7 +2387,19 @@ public final class PowerManagerService extends SystemService } else { EventLogTags.writeDeviceIdleOffPhase("power"); } + return true; } + return false; + } + } + + boolean setLightDeviceIdleModeInternal(boolean enabled) { + synchronized (mLock) { + if (mLightDeviceIdleMode != enabled) { + mLightDeviceIdleMode = enabled; + return true; + } + return false; } } @@ -2671,6 +2692,7 @@ public final class PowerManagerService extends SystemService pw.println(" mSandmanSummoned=" + mSandmanSummoned); pw.println(" mLowPowerModeEnabled=" + mLowPowerModeEnabled); pw.println(" mBatteryLevelLow=" + mBatteryLevelLow); + pw.println(" mLightDeviceIdleMode=" + mLightDeviceIdleMode); pw.println(" mDeviceIdleMode=" + mDeviceIdleMode); pw.println(" mDeviceIdleWhitelist=" + Arrays.toString(mDeviceIdleWhitelist)); pw.println(" mDeviceIdleTempWhitelist=" + Arrays.toString(mDeviceIdleTempWhitelist)); @@ -3308,6 +3330,16 @@ public final class PowerManagerService extends SystemService } } + @Override // Binder call + public boolean isLightDeviceIdleMode() { + final long ident = Binder.clearCallingIdentity(); + try { + return isLightDeviceIdleModeInternal(); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + /** * Reboots the device. * @@ -3576,8 +3608,13 @@ public final class PowerManagerService extends SystemService } @Override - public void setDeviceIdleMode(boolean enabled) { - setDeviceIdleModeInternal(enabled); + public boolean setDeviceIdleMode(boolean enabled) { + return setDeviceIdleModeInternal(enabled); + } + + @Override + public boolean setLightDeviceIdleMode(boolean enabled) { + return setLightDeviceIdleModeInternal(enabled); } @Override |