diff options
| -rw-r--r-- | packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java | 149 | ||||
| -rw-r--r-- | packages/SettingsLib/tests/unit/src/com/android/settingslib/fuelgague/BatteryStatusTest.kt | 330 |
2 files changed, 460 insertions, 19 deletions
diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java index 1251b0dc306a..9ab84d254ed6 100644 --- a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java +++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java @@ -26,6 +26,7 @@ import static android.os.BatteryManager.EXTRA_MAX_CHARGING_VOLTAGE; import static android.os.BatteryManager.EXTRA_PLUGGED; import static android.os.BatteryManager.EXTRA_PRESENT; import static android.os.BatteryManager.EXTRA_STATUS; +import static android.os.OsProtoEnums.BATTERY_PLUGGED_NONE; import android.content.Context; import android.content.Intent; @@ -40,6 +41,8 @@ import java.util.Optional; */ public class BatteryStatus { private static final int LOW_BATTERY_THRESHOLD = 20; + private static final int SEVERE_LOW_BATTERY_THRESHOLD = 10; + private static final int EXTREME_LOW_BATTERY_THRESHOLD = 3; private static final int DEFAULT_CHARGING_VOLTAGE_MICRO_VOLT = 5000000; public static final int CHARGING_UNKNOWN = -1; @@ -90,21 +93,7 @@ public class BatteryStatus { present = batteryChangedIntent.getBooleanExtra(EXTRA_PRESENT, true); this.incompatibleCharger = incompatibleCharger; - final int maxChargingMicroAmp = batteryChangedIntent.getIntExtra(EXTRA_MAX_CHARGING_CURRENT, - -1); - int maxChargingMicroVolt = batteryChangedIntent.getIntExtra(EXTRA_MAX_CHARGING_VOLTAGE, -1); - - if (maxChargingMicroVolt <= 0) { - maxChargingMicroVolt = DEFAULT_CHARGING_VOLTAGE_MICRO_VOLT; - } - if (maxChargingMicroAmp > 0) { - // Calculating muW = muA * muV / (10^6 mu^2 / mu); splitting up the divisor - // to maintain precision equally on both factors. - maxChargingWattage = (maxChargingMicroAmp / 1000) - * (maxChargingMicroVolt / 1000); - } else { - maxChargingWattage = -1; - } + maxChargingWattage = calculateMaxChargingMicroWatt(batteryChangedIntent); } /** Determine whether the device is plugged. */ @@ -126,7 +115,7 @@ public class BatteryStatus { /** Determine whether the device is plugged in dock. */ public boolean isPluggedInDock() { - return plugged == BatteryManager.BATTERY_PLUGGED_DOCK; + return isPluggedInDock(plugged); } /** @@ -140,15 +129,15 @@ public class BatteryStatus { /** Whether battery is low and needs to be charged. */ public boolean isBatteryLow() { - return level < LOW_BATTERY_THRESHOLD; + return isLowBattery(level); } /** Whether battery defender is enabled. */ public boolean isBatteryDefender() { - return chargingStatus == CHARGING_POLICY_ADAPTIVE_LONGLIFE; + return isBatteryDefender(chargingStatus); } - /** Return current chargin speed is fast, slow or normal. */ + /** Return current charging speed is fast, slow or normal. */ public final int getChargingSpeed(Context context) { final int slowThreshold = context.getResources().getInteger( R.integer.config_chargingSlowlyThreshold); @@ -218,4 +207,126 @@ public class BatteryStatus { || plugged == BatteryManager.BATTERY_PLUGGED_WIRELESS || plugged == BatteryManager.BATTERY_PLUGGED_DOCK; } + + /** Determine whether the device is plugged in dock. */ + public static boolean isPluggedInDock(Intent batteryChangedIntent) { + return isPluggedInDock( + batteryChangedIntent.getIntExtra(EXTRA_PLUGGED, BATTERY_PLUGGED_NONE)); + } + + /** Determine whether the device is plugged in dock. */ + public static boolean isPluggedInDock(int plugged) { + return plugged == BatteryManager.BATTERY_PLUGGED_DOCK; + } + + /** + * Whether the battery is low or not. + * + * @param batteryChangedIntent the {@link ACTION_BATTERY_CHANGED} intent + * @return {@code true} if the battery level is less or equal to {@link LOW_BATTERY_THRESHOLD} + */ + public static boolean isLowBattery(Intent batteryChangedIntent) { + int level = getBatteryLevel(batteryChangedIntent); + return isLowBattery(level); + } + + /** + * Whether the battery is low or not. + * + * @param batteryLevel the battery level + * @return {@code true} if the battery level is less or equal to {@link LOW_BATTERY_THRESHOLD} + */ + public static boolean isLowBattery(int batteryLevel) { + return batteryLevel <= LOW_BATTERY_THRESHOLD; + } + + /** + * Whether the battery is severe low or not. + * + * @param batteryChangedIntent the ACTION_BATTERY_CHANGED intent + * @return {@code true} if the battery level is less or equal to {@link + * SEVERE_LOW_BATTERY_THRESHOLD} + */ + public static boolean isSevereLowBattery(Intent batteryChangedIntent) { + int level = getBatteryLevel(batteryChangedIntent); + return level <= SEVERE_LOW_BATTERY_THRESHOLD; + } + + /** + * Whether the battery is extreme low or not. + * + * @param batteryChangedIntent the ACTION_BATTERY_CHANGED intent + * @return {@code true} if the battery level is less or equal to {@link + * EXTREME_LOW_BATTERY_THRESHOLD} + */ + public static boolean isExtremeLowBattery(Intent batteryChangedIntent) { + int level = getBatteryLevel(batteryChangedIntent); + return level <= EXTREME_LOW_BATTERY_THRESHOLD; + } + + /** + * Whether the battery defender is enabled or not. + * + * @param batteryChangedIntent the ACTION_BATTERY_CHANGED intent + * @return {@code true} if the battery defender is enabled. It could be dock defend, dwell + * defend, or temp defend + */ + public static boolean isBatteryDefender(Intent batteryChangedIntent) { + int chargingStatus = + batteryChangedIntent.getIntExtra(EXTRA_CHARGING_STATUS, CHARGING_POLICY_DEFAULT); + return isBatteryDefender(chargingStatus); + } + + /** + * Whether the battery defender is enabled or not. + * + * @param chargingStatus for {@link EXTRA_CHARGING_STATUS} field in the ACTION_BATTERY_CHANGED + * intent + * @return {@code true} if the battery defender is enabled. It could be dock defend, dwell + * defend, or temp defend + */ + public static boolean isBatteryDefender(int chargingStatus) { + return chargingStatus == CHARGING_POLICY_ADAPTIVE_LONGLIFE; + } + + /** + * Gets the max charging current and max charging voltage form {@link + * Intent.ACTION_BATTERY_CHANGED} and calculates the charging speed based on the {@link + * R.integer.config_chargingSlowlyThreshold} and {@link R.integer.config_chargingFastThreshold}. + * + * @param context the application context + * @param batteryChangedIntent the intent from {@link Intent.ACTION_BATTERY_CHANGED} + * @return the charging speed. {@link CHARGING_REGULAR}, {@link CHARGING_FAST}, {@link + * CHARGING_SLOWLY} or {@link CHARGING_UNKNOWN} + */ + public static int getChargingSpeed(Context context, Intent batteryChangedIntent) { + final int maxChargingMicroWatt = calculateMaxChargingMicroWatt(batteryChangedIntent); + if (maxChargingMicroWatt <= 0) { + return CHARGING_UNKNOWN; + } else if (maxChargingMicroWatt + < context.getResources().getInteger(R.integer.config_chargingSlowlyThreshold)) { + return CHARGING_SLOWLY; + } else if (maxChargingMicroWatt + > context.getResources().getInteger(R.integer.config_chargingFastThreshold)) { + return CHARGING_FAST; + } else { + return CHARGING_REGULAR; + } + } + + private static int calculateMaxChargingMicroWatt(Intent batteryChangedIntent) { + final int maxChargingMicroAmp = + batteryChangedIntent.getIntExtra(EXTRA_MAX_CHARGING_CURRENT, -1); + int maxChargingMicroVolt = batteryChangedIntent.getIntExtra(EXTRA_MAX_CHARGING_VOLTAGE, -1); + if (maxChargingMicroVolt <= 0) { + maxChargingMicroVolt = DEFAULT_CHARGING_VOLTAGE_MICRO_VOLT; + } + + if (maxChargingMicroAmp > 0) { + // Calculating µW = mA * mV + return (int) Math.round(maxChargingMicroAmp * 0.001 * maxChargingMicroVolt * 0.001); + } else { + return -1; + } + } } diff --git a/packages/SettingsLib/tests/unit/src/com/android/settingslib/fuelgague/BatteryStatusTest.kt b/packages/SettingsLib/tests/unit/src/com/android/settingslib/fuelgague/BatteryStatusTest.kt new file mode 100644 index 000000000000..6c0c1a73bd11 --- /dev/null +++ b/packages/SettingsLib/tests/unit/src/com/android/settingslib/fuelgague/BatteryStatusTest.kt @@ -0,0 +1,330 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.fuelgague + +import android.content.Context +import android.content.Intent +import android.os.BatteryManager +import android.os.BatteryManager.BATTERY_PLUGGED_AC +import android.os.BatteryManager.BATTERY_PLUGGED_DOCK +import android.os.BatteryManager.BATTERY_PLUGGED_USB +import android.os.BatteryManager.BATTERY_PLUGGED_WIRELESS +import android.os.BatteryManager.BATTERY_STATUS_FULL +import android.os.BatteryManager.BATTERY_STATUS_UNKNOWN +import android.os.BatteryManager.CHARGING_POLICY_ADAPTIVE_LONGLIFE +import android.os.BatteryManager.CHARGING_POLICY_DEFAULT +import android.os.BatteryManager.EXTRA_MAX_CHARGING_CURRENT +import android.os.BatteryManager.EXTRA_MAX_CHARGING_VOLTAGE +import android.os.OsProtoEnums.BATTERY_PLUGGED_NONE +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.settingslib.fuelgauge.BatteryStatus +import com.android.settingslib.fuelgauge.BatteryStatus.CHARGING_FAST +import com.android.settingslib.fuelgauge.BatteryStatus.CHARGING_REGULAR +import com.android.settingslib.fuelgauge.BatteryStatus.CHARGING_SLOWLY +import com.android.settingslib.fuelgauge.BatteryStatus.CHARGING_UNKNOWN +import com.android.settingslib.fuelgauge.BatteryStatus.isBatteryDefender +import com.google.common.truth.Truth.assertThat +import com.google.common.truth.Truth.assertWithMessage +import java.util.Optional +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.Parameterized +import org.junit.runners.Suite +import org.junit.runners.Suite.SuiteClasses + +@RunWith(Suite::class) +@SuiteClasses( + BatteryStatusTest.NonParameterizedTest::class, + BatteryStatusTest.IsPluggedInTest::class, + BatteryStatusTest.IsChargedTest::class, + BatteryStatusTest.GetChargingSpeedTest::class, + BatteryStatusTest.IsPluggedInDockTest::class, +) +open class BatteryStatusTest { + + @RunWith(AndroidJUnit4::class) + class NonParameterizedTest : BatteryStatusTest() { + @Test + fun isLowBattery_20Percent_returnsTrue() { + val level = 20 + val intent = createIntent(batteryLevel = level) + + assertWithMessage("failed by isLowBattery(Intent), level=$level") + .that(BatteryStatus.isLowBattery(intent)) + .isTrue() + assertWithMessage("failed by isLowBattery($level)") + .that(BatteryStatus.isLowBattery(level)) + .isTrue() + } + + @Test + fun isLowBattery_21Percent_returnsFalse() { + val level = 21 + val intent = createIntent(batteryLevel = level) + + assertWithMessage("failed by isLowBattery(intent), level=$level") + .that(BatteryStatus.isLowBattery(intent)) + .isFalse() + assertWithMessage("failed by isLowBattery($level)") + .that(BatteryStatus.isLowBattery(intent)) + .isFalse() + } + + @Test + fun isSevereLowBattery_10Percent_returnsTrue() { + val batteryChangedIntent = createIntent(batteryLevel = 10) + + assertThat(BatteryStatus.isSevereLowBattery(batteryChangedIntent)).isTrue() + } + + @Test + fun isSevereLowBattery_11Percent_returnFalse() { + val batteryChangedIntent = createIntent(batteryLevel = 11) + + assertThat(BatteryStatus.isSevereLowBattery(batteryChangedIntent)).isFalse() + } + + @Test + fun isExtremeLowBattery_3Percent_returnsTrue() { + val batteryChangedIntent = createIntent(batteryLevel = 3) + + assertThat(BatteryStatus.isExtremeLowBattery(batteryChangedIntent)).isTrue() + } + + @Test + fun isExtremeLowBattery_4Percent_returnsFalse() { + val batteryChangedIntent = createIntent(batteryLevel = 4) + + assertThat(BatteryStatus.isExtremeLowBattery(batteryChangedIntent)).isFalse() + } + + @Test + fun isBatteryDefender_chargingLongLife_returnsTrue() { + val chargingStatus = CHARGING_POLICY_ADAPTIVE_LONGLIFE + val batteryChangedIntent = createIntent(chargingStatus = chargingStatus) + + assertIsBatteryDefender(chargingStatus, batteryChangedIntent).isTrue() + } + + @Test + fun isBatteryDefender_nonChargingLongLife_returnsFalse() { + val chargingStatus = CHARGING_POLICY_DEFAULT + val batteryChangedIntent = createIntent(chargingStatus = chargingStatus) + + assertIsBatteryDefender(chargingStatus, batteryChangedIntent).isFalse() + } + + private fun assertIsBatteryDefender(chargingStatus: Int, batteryChangedIntent: Intent) = + object { + val assertions = + listOf( + "failed by isBatteryDefender(Intent), chargingStatus=$chargingStatus".let { + assertWithMessage(it).that(isBatteryDefender(batteryChangedIntent)) + }, + "failed by isBatteryDefender($chargingStatus)".let { + assertWithMessage(it).that(isBatteryDefender(chargingStatus)) + }, + ) + + fun isTrue() = assertions.forEach { it.isTrue() } + + fun isFalse() = assertions.forEach { it.isFalse() } + } + } + + @RunWith(Parameterized::class) + class IsPluggedInTest( + private val name: String, + private val plugged: Int, + val expected: Boolean + ) : BatteryStatusTest() { + + @Test + fun isPluggedIn_() { + val batteryChangedIntent = createIntent(plugged = plugged) + + assertWithMessage("failed by isPluggedIn(plugged=$plugged)") + .that(BatteryStatus.isPluggedIn(plugged)) + .isEqualTo(expected) + assertWithMessage("failed by isPlugged(Intent), which plugged=$plugged") + .that(BatteryStatus.isPluggedIn(batteryChangedIntent)) + .isEqualTo(expected) + } + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun parameters() = + arrayListOf( + arrayOf("withAC_returnsTrue", BATTERY_PLUGGED_AC, true), + arrayOf("withDock_returnsTrue", BATTERY_PLUGGED_DOCK, true), + arrayOf("withUSB_returnsTrue", BATTERY_PLUGGED_USB, true), + arrayOf("withWireless_returnsTrue", BATTERY_PLUGGED_WIRELESS, true), + arrayOf("pluggedNone_returnsTrue", BATTERY_PLUGGED_NONE, false), + ) + } + } + + @RunWith(Parameterized::class) + class IsPluggedInDockTest( + private val name: String, + private val plugged: Int, + val expected: Boolean + ) : BatteryStatusTest() { + + @Test + fun isPluggedDockIn_() { + val batteryChangedIntent = createIntent(plugged = plugged) + + assertWithMessage("failed by isPluggedInDock(plugged=$plugged)") + .that(BatteryStatus.isPluggedInDock(plugged)) + .isEqualTo(expected) + assertWithMessage("failed by isPluggedInDock(Intent), which plugged=$plugged") + .that(BatteryStatus.isPluggedInDock(batteryChangedIntent)) + .isEqualTo(expected) + } + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun parameters() = + arrayListOf( + arrayOf("withAC_returnsTrue", BATTERY_PLUGGED_AC, false), + arrayOf("withDock_returnsTrue", BATTERY_PLUGGED_DOCK, true), + arrayOf("withUSB_returnsTrue", BATTERY_PLUGGED_USB, false), + arrayOf("withWireless_returnsTrue", BATTERY_PLUGGED_WIRELESS, false), + arrayOf("pluggedNone_returnsTrue", BATTERY_PLUGGED_NONE, false), + ) + } + } + + @RunWith(Parameterized::class) + class IsChargedTest( + private val status: Int, + private val batteryLevel: Int, + private val expected: Boolean + ) : BatteryStatusTest() { + + @Test + fun isCharged_() { + val batteryChangedIntent = createIntent(batteryLevel = batteryLevel, status = status) + + assertWithMessage( + "failed by isCharged(Intent), status=$status, batteryLevel=$batteryLevel" + ) + .that(BatteryStatus.isCharged(batteryChangedIntent)) + .isEqualTo(expected) + assertWithMessage("failed by isCharged($status, $batteryLevel)") + .that(BatteryStatus.isCharged(status, batteryLevel)) + .isEqualTo(expected) + } + + companion object { + @Parameterized.Parameters(name = "status{0}_level{1}_returns-{2}") + @JvmStatic + fun parameters() = + arrayListOf( + arrayOf(BATTERY_STATUS_FULL, 99, true), + arrayOf(BATTERY_STATUS_UNKNOWN, 100, true), + arrayOf(BATTERY_STATUS_FULL, 100, true), + arrayOf(BATTERY_STATUS_UNKNOWN, 99, false), + ) + } + } + + @RunWith(Parameterized::class) + class GetChargingSpeedTest( + private val name: String, + private val maxChargingCurrent: Optional<Int>, + private val maxChargingVoltage: Optional<Int>, + private val expectedChargingSpeed: Int, + ) { + + val context: Context = ApplicationProvider.getApplicationContext() + + @Test + fun getChargingSpeed_() { + val batteryChangedIntent = + Intent(Intent.ACTION_BATTERY_CHANGED).apply { + maxChargingCurrent.ifPresent { putExtra(EXTRA_MAX_CHARGING_CURRENT, it) } + maxChargingVoltage.ifPresent { putExtra(EXTRA_MAX_CHARGING_VOLTAGE, it) } + } + + assertThat(BatteryStatus.getChargingSpeed(context, batteryChangedIntent)) + .isEqualTo(expectedChargingSpeed) + } + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun parameters() = + arrayListOf( + arrayOf( + "maxCurrent=n/a, maxVoltage=n/a -> UNKNOWN", + Optional.empty<Int>(), + Optional.empty<Int>(), + CHARGING_UNKNOWN + ), + arrayOf( + "maxCurrent=0, maxVoltage=9000000 -> UNKNOWN", + Optional.of(0), + Optional.of(0), + CHARGING_UNKNOWN + ), + arrayOf( + "maxCurrent=1500000, maxVoltage=5000000 -> CHARGING_REGULAR", + Optional.of(1500000), + Optional.of(5000000), + CHARGING_REGULAR + ), + arrayOf( + "maxCurrent=1000000, maxVoltage=5000000 -> CHARGING_REGULAR", + Optional.of(1000000), + Optional.of(5000000), + CHARGING_REGULAR + ), + arrayOf( + "maxCurrent=1500001, maxVoltage=5000000 -> CHARGING_FAST", + Optional.of(1501000), + Optional.of(5000000), + CHARGING_FAST + ), + arrayOf( + "maxCurrent=999999, maxVoltage=5000000 -> CHARGING_SLOWLY", + Optional.of(999999), + Optional.of(5000000), + CHARGING_SLOWLY + ), + ) + } + } + + protected fun createIntent( + batteryLevel: Int = 50, + chargingStatus: Int = CHARGING_POLICY_DEFAULT, + plugged: Int = BATTERY_PLUGGED_NONE, + status: Int = BatteryManager.BATTERY_STATUS_CHARGING, + ): Intent = + Intent(Intent.ACTION_BATTERY_CHANGED).apply { + putExtra(BatteryManager.EXTRA_STATUS, status) + putExtra(BatteryManager.EXTRA_LEVEL, batteryLevel) + putExtra(BatteryManager.EXTRA_SCALE, 100) + putExtra(BatteryManager.EXTRA_CHARGING_STATUS, chargingStatus) + putExtra(BatteryManager.EXTRA_PLUGGED, plugged) + } +} |