From f3fb895269deadcda0cf497d421e4b89e8ee8f8f Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Tue, 2 Oct 2012 20:57:05 -0700 Subject: [PATCH 1/2] Implement screen on hack for wireless chargers. We can't accurately detect whether the device is resting on a wireless charger unless it is actually charging. So we need to tweak the screen on when plugged / unplugged policy accordingly to avoid spurious wakeups. Bug: 7234284 Change-Id: I624b559e2e92b8813b12090bc20eca5f5158997e --- .../com/android/server/BatteryService.java | 9 +++ .../server/power/PowerManagerService.java | 59 +++++++++++++++++-- 2 files changed, 64 insertions(+), 4 deletions(-) diff --git a/services/java/com/android/server/BatteryService.java b/services/java/com/android/server/BatteryService.java index 4b20a53ba3ac0..0045f4a8d97fd 100644 --- a/services/java/com/android/server/BatteryService.java +++ b/services/java/com/android/server/BatteryService.java @@ -196,6 +196,15 @@ public final class BatteryService extends Binder { return false; } + /** + * Returns the current plug type. + */ + public int getPlugType() { + synchronized (mLock) { + return mPlugType; + } + } + /** * Returns battery level as a percentage. */ diff --git a/services/java/com/android/server/power/PowerManagerService.java b/services/java/com/android/server/power/PowerManagerService.java index a782d8886ac89..2ddda6cf651b5 100644 --- a/services/java/com/android/server/power/PowerManagerService.java +++ b/services/java/com/android/server/power/PowerManagerService.java @@ -135,6 +135,11 @@ public final class PowerManagerService extends IPowerManager.Stub // minimum screen off timeout should be longer than this. private static final int SCREEN_DIM_DURATION = 7 * 1000; + // Upper bound on the battery charge percentage in order to consider turning + // the screen on when the device starts charging wirelessly. + // See point of use for more details. + private static final int WIRELESS_CHARGER_TURN_ON_BATTERY_LEVEL_LIMIT = 95; + private Context mContext; private LightsService mLightsService; private BatteryService mBatteryService; @@ -218,6 +223,9 @@ public final class PowerManagerService extends IPowerManager.Stub // True if the device is plugged into a power source. private boolean mIsPowered; + // The current plug type, such as BatteryManager.BATTERY_PLUGGED_WIRELESS. + private int mPlugType; + // True if the device should wake up when plugged or unplugged. private boolean mWakeUpWhenPluggedOrUnpluggedConfig; @@ -1013,15 +1021,19 @@ public final class PowerManagerService extends IPowerManager.Stub */ private void updateIsPoweredLocked(int dirty) { if ((dirty & DIRTY_BATTERY_STATE) != 0) { - boolean wasPowered = mIsPowered; + final boolean wasPowered = mIsPowered; + final int oldPlugType = mPlugType; mIsPowered = mBatteryService.isPowered(BatteryManager.BATTERY_PLUGGED_ANY); + mPlugType = mBatteryService.getPlugType(); if (DEBUG) { Slog.d(TAG, "updateIsPoweredLocked: wasPowered=" + wasPowered - + ", mIsPowered=" + mIsPowered); + + ", mIsPowered=" + mIsPowered + + ", oldPlugType=" + oldPlugType + + ", mPlugType=" + mPlugType); } - if (wasPowered != mIsPowered) { + if (wasPowered != mIsPowered || oldPlugType != mPlugType) { mDirty |= DIRTY_IS_POWERED; // Treat plugging and unplugging the devices as a user activity. @@ -1030,7 +1042,7 @@ public final class PowerManagerService extends IPowerManager.Stub // Some devices also wake the device when plugged or unplugged because // they don't have a charging LED. final long now = SystemClock.uptimeMillis(); - if (mWakeUpWhenPluggedOrUnpluggedConfig) { + if (shouldWakeUpWhenPluggedOrUnpluggedLocked(wasPowered, oldPlugType)) { wakeUpNoUpdateLocked(now); } userActivityNoUpdateLocked( @@ -1039,6 +1051,44 @@ public final class PowerManagerService extends IPowerManager.Stub } } + private boolean shouldWakeUpWhenPluggedOrUnpluggedLocked(boolean wasPowered, int oldPlugType) { + if (mWakeUpWhenPluggedOrUnpluggedConfig) { + // FIXME: Need more accurate detection of wireless chargers. + // + // We are unable to accurately detect whether the device is resting on the + // charger unless it is actually receiving power. This causes us some grief + // because the device might not appear to be plugged into the wireless charger + // unless it actually charging. + // + // To avoid spuriously waking the screen, we apply a special policy to + // wireless chargers. + // + // 1. Don't wake the device when unplugged from wireless charger because + // it might be that the device is still resting on the wireless charger + // but is not receiving power anymore because the battery is full. + // + // 2. Don't wake the device when plugged into a wireless charger if the + // battery already appears to be mostly full. This situation may indicate + // that the device was resting on the charger the whole time and simply + // wasn't receiving power because the battery was full. We can't tell + // whether the device was just placed on the charger or whether it has + // been there for half of the night slowly discharging until it hit + // the point where it needed to start charging again. + if (wasPowered && !mIsPowered + && oldPlugType == BatteryManager.BATTERY_PLUGGED_WIRELESS) { + return false; + } + if (!wasPowered && mIsPowered + && mPlugType == BatteryManager.BATTERY_PLUGGED_WIRELESS + && mBatteryService.getBatteryLevel() >= + WIRELESS_CHARGER_TURN_ON_BATTERY_LEVEL_LIMIT) { + return false; + } + return true; + } + return false; + } + /** * Updates the value of mStayOn. * Sets DIRTY_STAY_ON if a change occurred. @@ -1891,6 +1941,7 @@ public final class PowerManagerService extends IPowerManager.Stub pw.println(" mDirty=0x" + Integer.toHexString(mDirty)); pw.println(" mWakefulness=" + wakefulnessToString(mWakefulness)); pw.println(" mIsPowered=" + mIsPowered); + pw.println(" mPlugType=" + mPlugType); pw.println(" mStayOn=" + mStayOn); pw.println(" mBootCompleted=" + mBootCompleted); pw.println(" mSystemReady=" + mSystemReady); From ff532540f18e3b2181da0550fe55045418a3b84e Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Tue, 2 Oct 2012 21:18:04 -0700 Subject: [PATCH 2/2] Improve the screen dim duration calculation. Take into account whether the screen off timeout is very short. If so, we use a shorter dim timeout. Don't allow the dim time to be more than 20% of the total screen on time so that the screen remains bright at least 80% of the time even when the timeout is short. Bug: 7273646 Change-Id: Iccea764b90f0d8b1df7009d26160c6bcf6eabe5b --- .../server/power/PowerManagerService.java | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/services/java/com/android/server/power/PowerManagerService.java b/services/java/com/android/server/power/PowerManagerService.java index 2ddda6cf651b5..d1c24ebd69ea5 100644 --- a/services/java/com/android/server/power/PowerManagerService.java +++ b/services/java/com/android/server/power/PowerManagerService.java @@ -130,11 +130,17 @@ public final class PowerManagerService extends IPowerManager.Stub private static final int DEFAULT_SCREEN_OFF_TIMEOUT = 15 * 1000; private static final int MINIMUM_SCREEN_OFF_TIMEOUT = 10 * 1000; - // The screen dim duration, in seconds. + // The screen dim duration, in milliseconds. // This is subtracted from the end of the screen off timeout so the // minimum screen off timeout should be longer than this. private static final int SCREEN_DIM_DURATION = 7 * 1000; + // The maximum screen dim time expressed as a ratio relative to the screen + // off timeout. If the screen off timeout is very short then we want the + // dim timeout to also be quite short so that most of the time is spent on. + // Otherwise the user won't get much screen on time before dimming occurs. + private static final float MAXIMUM_SCREEN_DIM_RATIO = 0.2f; + // Upper bound on the battery charge percentage in order to consider turning // the screen on when the device starts charging wirelessly. // See point of use for more details. @@ -1168,7 +1174,7 @@ public final class PowerManagerService extends IPowerManager.Stub long nextTimeout = 0; if (mWakefulness != WAKEFULNESS_ASLEEP) { final int screenOffTimeout = getScreenOffTimeoutLocked(); - final int screenDimDuration = getScreenDimDurationLocked(); + final int screenDimDuration = getScreenDimDurationLocked(screenOffTimeout); mUserActivitySummary = 0; if (mLastUserActivityTime >= mLastWakeTime) { @@ -1242,8 +1248,9 @@ public final class PowerManagerService extends IPowerManager.Stub return Math.max(timeout, MINIMUM_SCREEN_OFF_TIMEOUT); } - private int getScreenDimDurationLocked() { - return SCREEN_DIM_DURATION; + private int getScreenDimDurationLocked(int screenOffTimeout) { + return Math.min(SCREEN_DIM_DURATION, + (int)(screenOffTimeout * MAXIMUM_SCREEN_DIM_RATIO)); } /** @@ -1987,6 +1994,12 @@ public final class PowerManagerService extends IPowerManager.Stub pw.println(" mScreenBrightnessSettingMaximum=" + mScreenBrightnessSettingMaximum); pw.println(" mScreenBrightnessSettingDefault=" + mScreenBrightnessSettingDefault); + final int screenOffTimeout = getScreenOffTimeoutLocked(); + final int screenDimDuration = getScreenDimDurationLocked(screenOffTimeout); + pw.println(); + pw.println("Screen off timeout: " + screenOffTimeout + " ms"); + pw.println("Screen dim duration: " + screenDimDuration + " ms"); + pw.println(); pw.println("Wake Locks: size=" + mWakeLocks.size()); for (WakeLock wl : mWakeLocks) {