Implement auto-sleep functionality.
Added a new SLEEP_TIMEOUT setting which governs how long the device will
remain awake or dreaming without user activity. By default this
value is set to -1 which maintains today's existing behavior.
We basically represent the time we are allowed to be dreaming as a new
kind of user activity summary state called DREAM, similar to BRIGHT
and DIM. When the sleep timeout expires, the state is cleared and
the dream ends.
Bug: 17665809
Change-Id: I59aa7648dcec215f1285464fc1134934a09230e5
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index ec4a53e..e40c88f 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2001,7 +2001,10 @@
public static final String DIM_SCREEN = "dim_screen";
/**
- * The timeout before the screen turns off.
+ * The amount of time in milliseconds before the device goes to sleep or begins
+ * to dream after a period of inactivity. This value is also known as the
+ * user activity timeout period since the screen isn't necessarily turned off
+ * when it expires.
*/
public static final String SCREEN_OFF_TIMEOUT = "screen_off_timeout";
@@ -4817,6 +4820,20 @@
"usb_audio_automatic_routing_disabled";
/**
+ * The timeout in milliseconds before the device fully goes to sleep after
+ * a period of inactivity. This value sets an upper bound on how long the device
+ * will stay awake or dreaming without user activity. It should generally
+ * be longer than {@link #SCREEN_OFF_TIMEOUT} as otherwise the device
+ * will sleep before it ever has a chance to dream.
+ * <p>
+ * Use -1 to disable this timeout.
+ * </p>
+ *
+ * @hide
+ */
+ public static final String SLEEP_TIMEOUT = "sleep_timeout";
+
+ /**
* This are the settings to be backed up.
*
* NOTE: Settings are backed up and restored in the order they appear
@@ -4865,7 +4882,8 @@
MOUNT_UMS_AUTOSTART,
MOUNT_UMS_PROMPT,
MOUNT_UMS_NOTIFY_ENABLED,
- UI_NIGHT_MODE
+ UI_NIGHT_MODE,
+ SLEEP_TIMEOUT
};
/**
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 934ed38..efba03d 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -19,6 +19,7 @@
<resources>
<bool name="def_dim_screen">true</bool>
<integer name="def_screen_off_timeout">60000</integer>
+ <integer name="def_sleep_timeout">-1</integer>
<bool name="def_airplane_mode_on">false</bool>
<!-- Comma-separated list of bluetooth, wifi, and cell. -->
<string name="def_airplane_mode_radios" translatable="false">cell,bluetooth,wifi,nfc,wimax</string>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index 873257c..b17b4cc 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -70,7 +70,7 @@
// database gets upgraded properly. At a minimum, please confirm that 'upgradeVersion'
// is properly propagated through your change. Not doing so will result in a loss of user
// settings.
- private static final int DATABASE_VERSION = 112;
+ private static final int DATABASE_VERSION = 113;
private Context mContext;
private int mUserHandle;
@@ -1811,6 +1811,22 @@
upgradeVersion = 112;
}
+ if (upgradeVersion < 113) {
+ db.beginTransaction();
+ SQLiteStatement stmt = null;
+ try {
+ stmt = db.compileStatement("INSERT OR IGNORE INTO secure(name,value)"
+ + " VALUES(?,?);");
+ loadIntegerSetting(stmt, Settings.Secure.SLEEP_TIMEOUT,
+ R.integer.def_sleep_timeout);
+ db.setTransactionSuccessful();
+ } finally {
+ db.endTransaction();
+ if (stmt != null) stmt.close();
+ }
+ upgradeVersion = 113;
+ }
+
// *** Remember to update DATABASE_VERSION above!
if (upgradeVersion != currentVersion) {
@@ -2382,6 +2398,8 @@
loadBooleanSetting(stmt, Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
R.bool.def_lock_screen_allow_private_notifications);
+ loadIntegerSetting(stmt, Settings.Secure.SLEEP_TIMEOUT,
+ R.integer.def_sleep_timeout);
} finally {
if (stmt != null) stmt.close();
}
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 4f41bee..7ad5542f 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -142,10 +142,12 @@
// Summarizes the user activity state.
private static final int USER_ACTIVITY_SCREEN_BRIGHT = 1 << 0;
private static final int USER_ACTIVITY_SCREEN_DIM = 1 << 1;
+ private static final int USER_ACTIVITY_SCREEN_DREAM = 1 << 2;
// Default timeout in milliseconds. This is only used until the settings
// provider populates the actual default value (R.integer.def_screen_off_timeout).
private static final int DEFAULT_SCREEN_OFF_TIMEOUT = 15 * 1000;
+ private static final int DEFAULT_SLEEP_TIMEOUT = -1;
// Power hints defined in hardware/libhardware/include/hardware/power.h.
private static final int POWER_HINT_INTERACTION = 2;
@@ -214,7 +216,6 @@
private long mLastInteractivePowerHintTime;
// A bitfield that summarizes the effect of the user activity timer.
- // A zero value indicates that the user activity timer has expired.
private int mUserActivitySummary;
// The desired display power state. The actual state may lag behind the
@@ -340,6 +341,9 @@
// The screen off timeout setting value in milliseconds.
private int mScreenOffTimeoutSetting;
+ // The sleep timeout setting value in milliseconds.
+ private int mSleepTimeoutSetting;
+
// The maximum allowable screen off timeout according to the device
// administration policy. Overrides other settings.
private int mMaximumScreenOffTimeoutFromDeviceAdmin = Integer.MAX_VALUE;
@@ -543,6 +547,9 @@
resolver.registerContentObserver(Settings.System.getUriFor(
Settings.System.SCREEN_OFF_TIMEOUT),
false, mSettingsObserver, UserHandle.USER_ALL);
+ resolver.registerContentObserver(Settings.Secure.getUriFor(
+ Settings.Secure.SLEEP_TIMEOUT),
+ false, mSettingsObserver, UserHandle.USER_ALL);
resolver.registerContentObserver(Settings.Global.getUriFor(
Settings.Global.STAY_ON_WHILE_PLUGGED_IN),
false, mSettingsObserver, UserHandle.USER_ALL);
@@ -624,6 +631,9 @@
mScreenOffTimeoutSetting = Settings.System.getIntForUser(resolver,
Settings.System.SCREEN_OFF_TIMEOUT, DEFAULT_SCREEN_OFF_TIMEOUT,
UserHandle.USER_CURRENT);
+ mSleepTimeoutSetting = Settings.Secure.getIntForUser(resolver,
+ Settings.Secure.SLEEP_TIMEOUT, DEFAULT_SLEEP_TIMEOUT,
+ UserHandle.USER_CURRENT);
mStayOnWhilePluggedInSetting = Settings.Global.getInt(resolver,
Settings.Global.STAY_ON_WHILE_PLUGGED_IN, BatteryManager.BATTERY_PLUGGED_AC);
@@ -1431,7 +1441,8 @@
if (mWakefulness == WAKEFULNESS_AWAKE
|| mWakefulness == WAKEFULNESS_DREAMING
|| mWakefulness == WAKEFULNESS_DOZING) {
- final int screenOffTimeout = getScreenOffTimeoutLocked();
+ final int sleepTimeout = getSleepTimeoutLocked();
+ final int screenOffTimeout = getScreenOffTimeoutLocked(sleepTimeout);
final int screenDimDuration = getScreenDimDurationLocked(screenOffTimeout);
mUserActivitySummary = 0;
@@ -1439,11 +1450,11 @@
nextTimeout = mLastUserActivityTime
+ screenOffTimeout - screenDimDuration;
if (now < nextTimeout) {
- mUserActivitySummary |= USER_ACTIVITY_SCREEN_BRIGHT;
+ mUserActivitySummary = USER_ACTIVITY_SCREEN_BRIGHT;
} else {
nextTimeout = mLastUserActivityTime + screenOffTimeout;
if (now < nextTimeout) {
- mUserActivitySummary |= USER_ACTIVITY_SCREEN_DIM;
+ mUserActivitySummary = USER_ACTIVITY_SCREEN_DIM;
}
}
}
@@ -1458,7 +1469,22 @@
}
}
}
- if (mUserActivitySummary != 0) {
+ if (mUserActivitySummary == 0) {
+ if (sleepTimeout >= 0) {
+ final long anyUserActivity = Math.max(mLastUserActivityTime,
+ mLastUserActivityTimeNoChangeLights);
+ if (anyUserActivity >= mLastWakeTime) {
+ nextTimeout = anyUserActivity + sleepTimeout;
+ if (now < nextTimeout) {
+ mUserActivitySummary = USER_ACTIVITY_SCREEN_DREAM;
+ }
+ }
+ } else {
+ mUserActivitySummary = USER_ACTIVITY_SCREEN_DREAM;
+ nextTimeout = -1;
+ }
+ }
+ if (mUserActivitySummary != 0 && nextTimeout >= 0) {
Message msg = mHandler.obtainMessage(MSG_USER_ACTIVITY_TIMEOUT);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, nextTimeout);
@@ -1495,7 +1521,15 @@
}
}
- private int getScreenOffTimeoutLocked() {
+ private int getSleepTimeoutLocked() {
+ int timeout = mSleepTimeoutSetting;
+ if (timeout <= 0) {
+ return -1;
+ }
+ return Math.max(timeout, mMinimumScreenOffTimeoutConfig);
+ }
+
+ private int getScreenOffTimeoutLocked(int sleepTimeout) {
int timeout = mScreenOffTimeoutSetting;
if (isMaximumScreenOffTimeoutFromDeviceAdminEnforcedLocked()) {
timeout = Math.min(timeout, mMaximumScreenOffTimeoutFromDeviceAdmin);
@@ -1503,6 +1537,9 @@
if (mUserActivityTimeoutOverrideFromWindowManager >= 0) {
timeout = (int)Math.min(timeout, mUserActivityTimeoutOverrideFromWindowManager);
}
+ if (sleepTimeout >= 0) {
+ timeout = Math.min(timeout, sleepTimeout);
+ }
return Math.max(timeout, mMinimumScreenOffTimeoutConfig);
}
@@ -1619,8 +1656,7 @@
mSandmanScheduled = false;
wakefulness = mWakefulness;
if (mSandmanSummoned && mDisplayReady) {
- startDreaming = ((wakefulness == WAKEFULNESS_DREAMING && canDreamLocked())
- || wakefulness == WAKEFULNESS_DOZING);
+ startDreaming = canDreamLocked() || canDozeLocked();
mSandmanSummoned = false;
} else {
startDreaming = false;
@@ -1708,13 +1744,14 @@
/**
* Returns true if the device is allowed to dream in its current state.
- * This function is not called when dozing.
*/
private boolean canDreamLocked() {
if (mWakefulness != WAKEFULNESS_DREAMING
|| !mDreamsSupportedConfig
|| !mDreamsEnabledSetting
|| !mDisplayPowerRequest.isBrightOrDim()
+ || (mUserActivitySummary & (USER_ACTIVITY_SCREEN_BRIGHT
+ | USER_ACTIVITY_SCREEN_DIM | USER_ACTIVITY_SCREEN_DREAM)) == 0
|| !mBootCompleted) {
return false;
}
@@ -1737,6 +1774,13 @@
}
/**
+ * Returns true if the device is allowed to doze in its current state.
+ */
+ private boolean canDozeLocked() {
+ return mWakefulness == WAKEFULNESS_DOZING;
+ }
+
+ /**
* Updates the display power state asynchronously.
* When the update is finished, mDisplayReady will be set to true. The display
* controller posts a message to tell us when the actual display power state
@@ -2343,6 +2387,7 @@
pw.println(" mMaximumScreenDimDurationConfig=" + mMaximumScreenDimDurationConfig);
pw.println(" mMaximumScreenDimRatioConfig=" + mMaximumScreenDimRatioConfig);
pw.println(" mScreenOffTimeoutSetting=" + mScreenOffTimeoutSetting);
+ pw.println(" mSleepTimeoutSetting=" + mSleepTimeoutSetting);
pw.println(" mMaximumScreenOffTimeoutFromDeviceAdmin="
+ mMaximumScreenOffTimeoutFromDeviceAdmin + " (enforced="
+ isMaximumScreenOffTimeoutFromDeviceAdminEnforcedLocked() + ")");
@@ -2367,9 +2412,11 @@
pw.println(" mScreenBrightnessSettingMaximum=" + mScreenBrightnessSettingMaximum);
pw.println(" mScreenBrightnessSettingDefault=" + mScreenBrightnessSettingDefault);
- final int screenOffTimeout = getScreenOffTimeoutLocked();
+ final int sleepTimeout = getSleepTimeoutLocked();
+ final int screenOffTimeout = getScreenOffTimeoutLocked(sleepTimeout);
final int screenDimDuration = getScreenDimDurationLocked(screenOffTimeout);
pw.println();
+ pw.println("Sleep timeout: " + sleepTimeout + " ms");
pw.println("Screen off timeout: " + screenOffTimeout + " ms");
pw.println("Screen dim duration: " + screenDimDuration + " ms");