diff options
5 files changed, 96 insertions, 14 deletions
diff --git a/core/java/android/os/BatteryManagerInternal.java b/core/java/android/os/BatteryManagerInternal.java index 97ec5940ccb0..9bad0def0626 100644 --- a/core/java/android/os/BatteryManagerInternal.java +++ b/core/java/android/os/BatteryManagerInternal.java @@ -47,6 +47,14 @@ public abstract class BatteryManagerInternal { public abstract int getBatteryLevel(); /** + * Returns battery health status as an integer representing the current battery health constant. + * + * This is a simple accessor that's safe to be called from any locks, but internally it may + * wait on the battery service lock. + */ + public abstract int getBatteryHealth(); + + /** * Instantaneous battery capacity in uA-h, as defined in the HealthInfo HAL struct. * Please note apparently it could be bigger than {@link #getBatteryFullCharge}. * diff --git a/core/proto/android/server/powermanagerservice.proto b/core/proto/android/server/powermanagerservice.proto index bd4f99044b17..004d5d699a31 100644 --- a/core/proto/android/server/powermanagerservice.proto +++ b/core/proto/android/server/powermanagerservice.proto @@ -189,6 +189,8 @@ message PowerManagerServiceDumpProto { optional bool is_enhanced_discharge_prediction_personalized = 54; optional bool is_low_power_standby_active = 55; optional LowPowerStandbyControllerDumpProto low_power_standby_controller = 56; + // The battery level drained by the dream. + optional int32 battery_level_drained_while_dreaming = 57; } // A com.android.server.power.PowerManagerService.SuspendBlockerImpl object. diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java index 4278b3edc3eb..71a4c73a4dac 100644 --- a/services/core/java/com/android/server/BatteryService.java +++ b/services/core/java/com/android/server/BatteryService.java @@ -1306,6 +1306,13 @@ public final class BatteryService extends SystemService { } @Override + public int getBatteryHealth() { + synchronized (mLock) { + return mHealthInfo.batteryHealth; + } + } + + @Override public boolean getBatteryLevelLow() { synchronized (mLock) { return mBatteryLevelLow; diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index 20235eb2c464..9281f4bf75b0 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -423,6 +423,9 @@ public final class PowerManagerService extends SystemService // The current battery level percentage. private int mBatteryLevel; + // The amount of battery drained while the device has been in a dream state. + private int mDreamsBatteryLevelDrain; + // True if updatePowerStateLocked() is already in progress. // TODO(b/215518989): Remove this once transactions are in place private boolean mUpdatePowerStateInProgress; @@ -455,11 +458,6 @@ public final class PowerManagerService extends SystemService @GuardedBy("mEnhancedDischargeTimeLock") private boolean mEnhancedDischargePredictionIsPersonalized; - // The battery level percentage at the time the dream started. - // This is used to terminate a dream and go to sleep if the battery is - // draining faster than it is charging and the user activity timeout has expired. - private int mBatteryLevelWhenDreamStarted; - // The current dock state. private int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED; @@ -2469,15 +2467,25 @@ public final class PowerManagerService extends SystemService final int oldPlugType = mPlugType; mIsPowered = mBatteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_ANY); mPlugType = mBatteryManagerInternal.getPlugType(); + final int oldBatteryLevel = mBatteryLevel; mBatteryLevel = mBatteryManagerInternal.getBatteryLevel(); mBatteryLevelLow = mBatteryManagerInternal.getBatteryLevelLow(); + final boolean isOverheat = mBatteryManagerInternal.getBatteryHealth() + == BatteryManager.BATTERY_HEALTH_OVERHEAT; if (DEBUG_SPEW) { Slog.d(TAG, "updateIsPoweredLocked: wasPowered=" + wasPowered + ", mIsPowered=" + mIsPowered + ", oldPlugType=" + oldPlugType + ", mPlugType=" + mPlugType - + ", mBatteryLevel=" + mBatteryLevel); + + ", oldBatteryLevel=" + oldBatteryLevel + + ", mBatteryLevel=" + mBatteryLevel + + ", isOverheat=" + isOverheat); + } + + if (!isOverheat && oldBatteryLevel > 0 + && getGlobalWakefulnessLocked() == WAKEFULNESS_DREAMING) { + mDreamsBatteryLevelDrain += (oldBatteryLevel - mBatteryLevel); } if (wasPowered != mIsPowered || oldPlugType != mPlugType) { @@ -3300,7 +3308,7 @@ public final class PowerManagerService extends SystemService // Remember the initial battery level when the dream started. if (startDreaming && isDreaming) { - mBatteryLevelWhenDreamStarted = mBatteryLevel; + mDreamsBatteryLevelDrain = 0; if (wakefulness == WAKEFULNESS_DOZING) { Slog.i(TAG, "Dozing..."); } else { @@ -3321,16 +3329,15 @@ public final class PowerManagerService extends SystemService if (wakefulness == WAKEFULNESS_DREAMING) { if (isDreaming && canDreamLocked(powerGroup)) { if (mDreamsBatteryLevelDrainCutoffConfig >= 0 - && mBatteryLevel < mBatteryLevelWhenDreamStarted - - mDreamsBatteryLevelDrainCutoffConfig + && mDreamsBatteryLevelDrain > mDreamsBatteryLevelDrainCutoffConfig && !isBeingKeptAwakeLocked(powerGroup)) { // If the user activity timeout expired and the battery appears // to be draining faster than it is charging then stop dreaming // and go to sleep. Slog.i(TAG, "Stopping dream because the battery appears to " + "be draining faster than it is charging. " - + "Battery level when dream started: " - + mBatteryLevelWhenDreamStarted + "%. " + + "Battery level drained while dreaming: " + + mDreamsBatteryLevelDrain + "%. " + "Battery level now: " + mBatteryLevel + "%."); } else { return; // continue dreaming @@ -3561,6 +3568,11 @@ public final class PowerManagerService extends SystemService mScreenBrightnessBoostInProgress); } + @VisibleForTesting + int getDreamsBatteryLevelDrain() { + return mDreamsBatteryLevelDrain; + } + private final DisplayManagerInternal.DisplayPowerCallbacks mDisplayPowerCallbacks = new DisplayManagerInternal.DisplayPowerCallbacks() { @@ -4405,7 +4417,7 @@ public final class PowerManagerService extends SystemService pw.println(" mIsPowered=" + mIsPowered); pw.println(" mPlugType=" + mPlugType); pw.println(" mBatteryLevel=" + mBatteryLevel); - pw.println(" mBatteryLevelWhenDreamStarted=" + mBatteryLevelWhenDreamStarted); + pw.println(" mDreamsBatteryLevelDrain=" + mDreamsBatteryLevelDrain); pw.println(" mDockState=" + mDockState); pw.println(" mStayOn=" + mStayOn); pw.println(" mProximityPositive=" + mProximityPositive); @@ -4650,8 +4662,8 @@ public final class PowerManagerService extends SystemService proto.write(PowerManagerServiceDumpProto.PLUG_TYPE, mPlugType); proto.write(PowerManagerServiceDumpProto.BATTERY_LEVEL, mBatteryLevel); proto.write( - PowerManagerServiceDumpProto.BATTERY_LEVEL_WHEN_DREAM_STARTED, - mBatteryLevelWhenDreamStarted); + PowerManagerServiceDumpProto.BATTERY_LEVEL_DRAINED_WHILE_DREAMING, + mDreamsBatteryLevelDrain); proto.write(PowerManagerServiceDumpProto.DOCK_STATE, mDockState); proto.write(PowerManagerServiceDumpProto.IS_STAY_ON, mStayOn); proto.write(PowerManagerServiceDumpProto.IS_PROXIMITY_POSITIVE, mProximityPositive); diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java index d0d2b412b1b6..c81db9216c08 100644 --- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java @@ -389,6 +389,18 @@ public class PowerManagerServiceTest { mBatteryReceiver.onReceive(mContextSpy, new Intent(Intent.ACTION_BATTERY_CHANGED)); } + private void setBatteryLevel(int batteryLevel) { + when(mBatteryManagerInternalMock.getBatteryLevel()) + .thenReturn(batteryLevel); + mBatteryReceiver.onReceive(mContextSpy, new Intent(Intent.ACTION_BATTERY_CHANGED)); + } + + private void setBatteryHealth(int batteryHealth) { + when(mBatteryManagerInternalMock.getBatteryHealth()) + .thenReturn(batteryHealth); + mBatteryReceiver.onReceive(mContextSpy, new Intent(Intent.ACTION_BATTERY_CHANGED)); + } + private void setAttentiveTimeout(int attentiveTimeoutMillis) { Settings.Secure.putInt( mContextSpy.getContentResolver(), Settings.Secure.ATTENTIVE_TIMEOUT, @@ -413,6 +425,12 @@ public class PowerManagerServiceTest { .thenReturn(disable); } + private void setDreamsBatteryLevelDrainConfig(int threshold) { + when(mResourcesSpy.getInteger( + com.android.internal.R.integer.config_dreamsBatteryLevelDrainCutoff)).thenReturn( + threshold); + } + private void advanceTime(long timeMs) { mClock.fastForward(timeMs); mTestLooper.dispatchAll(); @@ -987,6 +1005,41 @@ public class PowerManagerServiceTest { } @Test + public void testBatteryDrainDuringDream() { + Settings.Secure.putInt(mContextSpy.getContentResolver(), + Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP, 1); + Settings.Secure.putInt(mContextSpy.getContentResolver(), + Settings.Secure.SCREENSAVER_ENABLED, 1); + + setMinimumScreenOffTimeoutConfig(100); + setDreamsBatteryLevelDrainConfig(5); + createService(); + startSystem(); + + doAnswer(inv -> { + when(mDreamManagerInternalMock.isDreaming()).thenReturn(true); + return null; + }).when(mDreamManagerInternalMock).startDream(anyBoolean(), anyString()); + + setBatteryLevel(100); + setPluggedIn(true); + + forceAwake(); // Needs to be awake first before it can dream. + forceDream(); + advanceTime(10); // Allow async calls to happen + assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_DREAMING); + setBatteryLevel(90); + advanceTime(10); // Allow async calls to happen + assertThat(mService.getDreamsBatteryLevelDrain()).isEqualTo(10); + + // If battery overheat protection is enabled, we shouldn't count battery drain + setBatteryHealth(BatteryManager.BATTERY_HEALTH_OVERHEAT); + setBatteryLevel(70); + advanceTime(10); // Allow async calls to happen + assertThat(mService.getDreamsBatteryLevelDrain()).isEqualTo(10); + } + + @Test public void testSetDozeOverrideFromDreamManager_triggersSuspendBlocker() { final String suspendBlockerName = "PowerManagerService.Display"; final String tag = "acq_causes_wakeup"; |