Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 36307968 authored by Salvador Martinez's avatar Salvador Martinez
Browse files

Update time based triggering logic to guard against spam

Since the time based estimate takes recent usage into account we
could get a situation where the notification could trigger many
times. This CL makes it so the notification can only trigger at most
twice per charge cycle. The percentage based triggering remains
unaffected.

Test: SysUI Tests
Bug: 27567513
Change-Id: I81e29f96ac97b0d1a177e923c16d5f2800da404a
parent 4d0e87d9
Loading
Loading
Loading
Loading
+40 −16
Original line number Diff line number Diff line
@@ -53,7 +53,6 @@ import com.android.systemui.statusbar.phone.StatusBar;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;

public class PowerUI extends SystemUI {
    static final String TAG = "PowerUI";
@@ -72,10 +71,11 @@ public class PowerUI extends SystemUI {
    private final Configuration mLastConfiguration = new Configuration();
    private int mBatteryLevel = 100;
    private long mTimeRemaining = Long.MAX_VALUE;
    private int mBatteryStatus = BatteryManager.BATTERY_STATUS_UNKNOWN;
    private int mPlugType = 0;
    private int mInvalidCharger = 0;
    private EnhancedEstimates mEnhancedEstimates;
    private boolean mLowWarningShownThisChargeCycle;
    private boolean mSevereWarningShownThisChargeCycle;

    private int mLowBatteryAlertCloseLevel;
    private final int[] mLowBatteryReminderLevels = new int[2];
@@ -88,6 +88,8 @@ public class PowerUI extends SystemUI {
    private long mNextLogTime;
    private IThermalService mThermalService;

    @VisibleForTesting int mBatteryStatus = BatteryManager.BATTERY_STATUS_UNKNOWN;

    // by using the same instance (method references are not guaranteed to be the same object
    // We create a method reference here so that we are guaranteed that we can remove a callback
    // each time they are created).
@@ -218,6 +220,12 @@ public class PowerUI extends SystemUI {

                final boolean plugged = mPlugType != 0;
                final boolean oldPlugged = oldPlugType != 0;
                // if we are now unplugged but we were previously plugged in we should allow the
                // time based trigger again.
                if (!plugged && plugged != oldPlugged) {
                    mLowWarningShownThisChargeCycle = false;
                    mSevereWarningShownThisChargeCycle = false;
                }

                int oldBucket = findBatteryLevelBucket(oldBatteryLevel);
                int bucket = findBatteryLevelBucket(mBatteryLevel);
@@ -268,7 +276,6 @@ public class PowerUI extends SystemUI {
        boolean isPowerSaver = mPowerManager.isPowerSaveMode();
        // only play SFX when the dialog comes up or the bucket changes
        final boolean playSound = bucket != oldBucket || oldPlugged;
        long oldTimeRemaining = mTimeRemaining;
        if (mEnhancedEstimates.isHybridNotificationEnabled()) {
            final Estimate estimate = mEnhancedEstimates.getEstimate();
            // Turbo is not always booted once SysUI is running so we have ot make sure we actually
@@ -281,10 +288,18 @@ public class PowerUI extends SystemUI {
            }
        }

        if (shouldShowLowBatteryWarning(plugged, oldPlugged, oldBucket, bucket, oldTimeRemaining,
                mTimeRemaining,
                isPowerSaver, mBatteryStatus)) {
        if (shouldShowLowBatteryWarning(plugged, oldPlugged, oldBucket, bucket,
                mTimeRemaining, isPowerSaver, mBatteryStatus)) {
            mWarnings.showLowBatteryWarning(playSound);

            // mark if we've already shown a warning this cycle. This will prevent the time based
            // trigger from spamming users since the time remaining can vary based on current
            // device usage.
            if (mTimeRemaining < mEnhancedEstimates.getSevereWarningThreshold()) {
                mSevereWarningShownThisChargeCycle = true;
            } else {
                mLowWarningShownThisChargeCycle = true;
            }
        } else if (shouldDismissLowBatteryWarning(plugged, oldBucket, bucket, mTimeRemaining,
                isPowerSaver)) {
            mWarnings.dismissLowBatteryWarning();
@@ -295,22 +310,14 @@ public class PowerUI extends SystemUI {

    @VisibleForTesting
    boolean shouldShowLowBatteryWarning(boolean plugged, boolean oldPlugged, int oldBucket,
            int bucket, long oldTimeRemaining, long timeRemaining,
            boolean isPowerSaver, int mBatteryStatus) {
            int bucket, long timeRemaining, boolean isPowerSaver, int mBatteryStatus) {
        return !plugged
                && !isPowerSaver
                && (((bucket < oldBucket || oldPlugged) && bucket < 0)
                        || (mEnhancedEstimates.isHybridNotificationEnabled()
                                && timeRemaining < mEnhancedEstimates.getLowWarningThreshold()
                                && isHourLess(oldTimeRemaining, timeRemaining)))
                        || isTimeBasedTrigger(timeRemaining))
                && mBatteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN;
    }

    private boolean isHourLess(long oldTimeRemaining, long timeRemaining) {
        final long dif = oldTimeRemaining - timeRemaining;
        return dif >= TimeUnit.HOURS.toMillis(1);
    }

    @VisibleForTesting
    boolean shouldDismissLowBatteryWarning(boolean plugged, int oldBucket, int bucket,
            long timeRemaining, boolean isPowerSaver) {
@@ -323,6 +330,23 @@ public class PowerUI extends SystemUI {
                        || hybridWouldDismiss));
    }

    private boolean isTimeBasedTrigger(long timeRemaining) {
        if (!mEnhancedEstimates.isHybridNotificationEnabled()) {
            return false;
        }

        // Only show the time based warning once per charge cycle
        final boolean canShowWarning = timeRemaining < mEnhancedEstimates.getLowWarningThreshold()
                && !mLowWarningShownThisChargeCycle;

        // Only show the severe time based warning once per charge cycle
        final boolean canShowSevereWarning =
                timeRemaining < mEnhancedEstimates.getSevereWarningThreshold()
                        && !mSevereWarningShownThisChargeCycle;

        return canShowWarning || canShowSevereWarning;
    }

    private void initTemperatureWarning() {
        ContentResolver resolver = mContext.getContentResolver();
        Resources resources = mContext.getResources();
+28 −9
Original line number Diff line number Diff line
@@ -155,7 +155,7 @@ public class PowerUITest extends SysuiTestCase {
        // hybrid but the threshold has been overriden to be too low
        boolean shouldShow =
                mPowerUI.shouldShowLowBatteryWarning(UNPLUGGED, UNPLUGGED, ABOVE_WARNING_BUCKET,
                        ABOVE_WARNING_BUCKET, Long.MAX_VALUE, BELOW_HYBRID_THRESHOLD,
                        ABOVE_WARNING_BUCKET, BELOW_HYBRID_THRESHOLD,
                        POWER_SAVER_OFF, BatteryManager.BATTERY_HEALTH_GOOD);
        assertFalse(shouldShow);
    }
@@ -172,7 +172,7 @@ public class PowerUITest extends SysuiTestCase {
        // hybrid since the threshold has been overriden to be much higher
        boolean shouldShow =
                mPowerUI.shouldShowLowBatteryWarning(UNPLUGGED, UNPLUGGED, ABOVE_WARNING_BUCKET,
                        ABOVE_WARNING_BUCKET, Long.MAX_VALUE, ABOVE_HYBRID_THRESHOLD,
                        ABOVE_WARNING_BUCKET, ABOVE_HYBRID_THRESHOLD,
                        POWER_SAVER_OFF, BatteryManager.BATTERY_HEALTH_GOOD);
        assertTrue(shouldShow);
    }
@@ -188,7 +188,7 @@ public class PowerUITest extends SysuiTestCase {
        // hybrid
        boolean shouldShow =
                mPowerUI.shouldShowLowBatteryWarning(UNPLUGGED, UNPLUGGED, ABOVE_WARNING_BUCKET,
                        ABOVE_WARNING_BUCKET, Long.MAX_VALUE, BELOW_HYBRID_THRESHOLD,
                        ABOVE_WARNING_BUCKET, BELOW_HYBRID_THRESHOLD,
                        POWER_SAVER_OFF, BatteryManager.BATTERY_HEALTH_GOOD);
        assertTrue(shouldShow);
    }
@@ -203,7 +203,7 @@ public class PowerUITest extends SysuiTestCase {
        // unplugged device that would show the non-hybrid notification and the hybrid
        boolean shouldShow =
                mPowerUI.shouldShowLowBatteryWarning(UNPLUGGED, UNPLUGGED, ABOVE_WARNING_BUCKET,
                        BELOW_WARNING_BUCKET, Long.MAX_VALUE, BELOW_HYBRID_THRESHOLD,
                        BELOW_WARNING_BUCKET, BELOW_HYBRID_THRESHOLD,
                        POWER_SAVER_OFF, BatteryManager.BATTERY_HEALTH_GOOD);
        assertTrue(shouldShow);
    }
@@ -218,7 +218,7 @@ public class PowerUITest extends SysuiTestCase {
        // unplugged device that would show the non-hybrid but not the hybrid
        boolean shouldShow =
                mPowerUI.shouldShowLowBatteryWarning(UNPLUGGED, UNPLUGGED, ABOVE_WARNING_BUCKET,
                        BELOW_WARNING_BUCKET, Long.MAX_VALUE, ABOVE_HYBRID_THRESHOLD,
                        BELOW_WARNING_BUCKET, ABOVE_HYBRID_THRESHOLD,
                        POWER_SAVER_OFF, BatteryManager.BATTERY_HEALTH_GOOD);
        assertTrue(shouldShow);
    }
@@ -233,7 +233,7 @@ public class PowerUITest extends SysuiTestCase {
        // unplugged device that would show the neither due to battery level being good
        boolean shouldShow =
                mPowerUI.shouldShowLowBatteryWarning(UNPLUGGED, UNPLUGGED, ABOVE_WARNING_BUCKET,
                        ABOVE_WARNING_BUCKET, ABOVE_HYBRID_THRESHOLD, ABOVE_HYBRID_THRESHOLD,
                        ABOVE_WARNING_BUCKET, ABOVE_HYBRID_THRESHOLD,
                        POWER_SAVER_OFF, BatteryManager.BATTERY_HEALTH_GOOD);
        assertFalse(shouldShow);
    }
@@ -248,7 +248,7 @@ public class PowerUITest extends SysuiTestCase {
        // plugged device that would show the neither due to being plugged
        boolean shouldShow =
                mPowerUI.shouldShowLowBatteryWarning(!UNPLUGGED, UNPLUGGED, ABOVE_WARNING_BUCKET,
                        BELOW_WARNING_BUCKET, ABOVE_HYBRID_THRESHOLD, BELOW_HYBRID_THRESHOLD,
                        BELOW_WARNING_BUCKET, BELOW_HYBRID_THRESHOLD,
                        POWER_SAVER_OFF, BatteryManager.BATTERY_HEALTH_GOOD);
        assertFalse(shouldShow);
   }
@@ -263,7 +263,7 @@ public class PowerUITest extends SysuiTestCase {
        // Unknown battery status device that would show the neither due
        boolean shouldShow =
                mPowerUI.shouldShowLowBatteryWarning(UNPLUGGED, UNPLUGGED, ABOVE_WARNING_BUCKET,
                        BELOW_WARNING_BUCKET, ABOVE_HYBRID_THRESHOLD, BELOW_HYBRID_THRESHOLD,
                        BELOW_WARNING_BUCKET, BELOW_HYBRID_THRESHOLD,
                        !POWER_SAVER_OFF, BatteryManager.BATTERY_STATUS_UNKNOWN);
        assertFalse(shouldShow);
    }
@@ -278,11 +278,30 @@ public class PowerUITest extends SysuiTestCase {
        // BatterySaverEnabled device that would show the neither due to battery saver
        boolean shouldShow =
                mPowerUI.shouldShowLowBatteryWarning(UNPLUGGED, UNPLUGGED, ABOVE_WARNING_BUCKET,
                        BELOW_WARNING_BUCKET, ABOVE_HYBRID_THRESHOLD, BELOW_HYBRID_THRESHOLD,
                        BELOW_WARNING_BUCKET, BELOW_HYBRID_THRESHOLD,
                        !POWER_SAVER_OFF, BatteryManager.BATTERY_HEALTH_GOOD);
        assertFalse(shouldShow);
    }

    @Test
    public void testShouldShowLowBatteryWarning_onlyShowsOncePerChargeCycle() {
        mPowerUI.start();
        when(mEnhancedEstimates.isHybridNotificationEnabled()).thenReturn(true);
        when(mEnhancedEstimates.getLowWarningThreshold()).thenReturn(PowerUI.THREE_HOURS_IN_MILLIS);
        when(mEnhancedEstimates.getSevereWarningThreshold()).thenReturn(ONE_HOUR_MILLIS);
        when(mEnhancedEstimates.getEstimate())
                .thenReturn(new Estimate(BELOW_HYBRID_THRESHOLD, true));
        mPowerUI.mBatteryStatus = BatteryManager.BATTERY_HEALTH_GOOD;

        mPowerUI.maybeShowBatteryWarning(UNPLUGGED, UNPLUGGED, ABOVE_WARNING_BUCKET,
                ABOVE_WARNING_BUCKET);
        boolean shouldShow =
                mPowerUI.shouldShowLowBatteryWarning(UNPLUGGED, UNPLUGGED, ABOVE_WARNING_BUCKET,
                        ABOVE_WARNING_BUCKET, BELOW_HYBRID_THRESHOLD,
                        POWER_SAVER_OFF, BatteryManager.BATTERY_HEALTH_GOOD);
        assertFalse(shouldShow);
    }

    @Test
    public void testShouldDismissLowBatteryWarning_dismissWhenPowerSaverEnabled() {
        mPowerUI.start();