From a233bba91e64b6441d363a6485aff58d72f815ef Mon Sep 17 00:00:00 2001 From: Lucas Silva Date: Mon, 14 Nov 2022 22:53:07 -0500 Subject: [PATCH] Update dream battery drain monitoring to exclude when charging is being artificially limited. We may limit charging when battery is overheating, or due to dock defend or other battery defend mechanisms. In these cases, the battery is expected to drain while dreaming since charging is limited. Therefore, do not exit dreams in this case. Test: atest PowerManagerServiceTest Fixes: 258027228 Change-Id: I10ce0c848892b42267d819d07a284d17a97899d1 --- .../android/os/BatteryManagerInternal.java | 8 +++ .../android/server/powermanagerservice.proto | 2 + .../com/android/server/BatteryService.java | 7 +++ .../server/power/PowerManagerService.java | 40 +++++++++----- .../server/power/PowerManagerServiceTest.java | 53 +++++++++++++++++++ 5 files changed, 96 insertions(+), 14 deletions(-) diff --git a/core/java/android/os/BatteryManagerInternal.java b/core/java/android/os/BatteryManagerInternal.java index 97ec5940ccb06..9bad0def06262 100644 --- a/core/java/android/os/BatteryManagerInternal.java +++ b/core/java/android/os/BatteryManagerInternal.java @@ -46,6 +46,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 bd4f99044b176..004d5d699a31d 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 2f8dea7b5f14f..e282679d86953 100644 --- a/services/core/java/com/android/server/BatteryService.java +++ b/services/core/java/com/android/server/BatteryService.java @@ -1286,6 +1286,13 @@ public final class BatteryService extends SystemService { } } + @Override + public int getBatteryHealth() { + synchronized (mLock) { + return mHealthInfo.batteryHealth; + } + } + @Override public boolean getBatteryLevelLow() { synchronized (mLock) { diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index 377a651eb0313..8056d5430a8fa 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -419,6 +419,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; @@ -451,11 +454,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; @@ -2448,15 +2446,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) { @@ -3278,7 +3286,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 { @@ -3299,16 +3307,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 @@ -3539,6 +3546,11 @@ public final class PowerManagerService extends SystemService mScreenBrightnessBoostInProgress); } + @VisibleForTesting + int getDreamsBatteryLevelDrain() { + return mDreamsBatteryLevelDrain; + } + private final DisplayManagerInternal.DisplayPowerCallbacks mDisplayPowerCallbacks = new DisplayManagerInternal.DisplayPowerCallbacks() { @@ -4383,7 +4395,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); @@ -4625,8 +4637,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 f5ed41a7fa8b5..954e63bdfffe2 100644 --- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java @@ -375,6 +375,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, @@ -399,6 +411,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(); @@ -881,6 +899,41 @@ public class PowerManagerServiceTest { assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_DREAMING); } + @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";