diff --git a/src/com/android/settings/fuelgauge/BatteryUtils.java b/src/com/android/settings/fuelgauge/BatteryUtils.java index da9b7059908..10bc85343e9 100644 --- a/src/com/android/settings/fuelgauge/BatteryUtils.java +++ b/src/com/android/settings/fuelgauge/BatteryUtils.java @@ -44,6 +44,7 @@ import com.android.settings.fuelgauge.anomaly.Anomaly; import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.utils.PowerUtil; + import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Collections; @@ -69,6 +70,7 @@ public class BatteryUtils { int BACKGROUND = 2; int ALL = 3; } + private static final String TAG = "BatteryUtils"; private static final int MIN_POWER_THRESHOLD_MILLI_AMP = 5; @@ -81,6 +83,7 @@ public class BatteryUtils { private Context mContext; @VisibleForTesting PowerUsageFeatureProvider mPowerUsageFeatureProvider; + public static BatteryUtils getInstance(Context context) { if (sInstance == null || sInstance.isDataCorrupted()) { sInstance = new BatteryUtils(context); @@ -153,8 +156,7 @@ public class BatteryUtils { private long getProcessForegroundTimeMs(BatteryStats.Uid uid, int which) { final long rawRealTimeUs = PowerUtil.convertMsToUs(SystemClock.elapsedRealtime()); return getScreenUsageTimeMs(uid, which, rawRealTimeUs) - + PowerUtil.convertUsToMs( - getForegroundServiceTotalTimeUs(uid, rawRealTimeUs)); + + PowerUtil.convertUsToMs(getForegroundServiceTotalTimeUs(uid, rawRealTimeUs)); } /** @@ -349,6 +351,7 @@ public class BatteryUtils { /** * Calculate the screen usage time since last full charge. + * * @param batteryStatsHelper utility class that contains the screen usage data * @return time in millis */ @@ -500,5 +503,35 @@ public class BatteryUtils { return false; } + /** + * Check if the app represented by {@code uid} has battery usage more than {@code threshold} + * + * @param batteryStatsHelper used to check the battery usage + * @param userManager used to init the {@code batteryStatsHelper} + * @param uid represent the app + * @param threshold battery percentage threshold(e.g. 10 means 10% battery usage ) + * @return {@code true} if battery drain is more than the threshold + */ + public boolean isAppHeavilyUsed(BatteryStatsHelper batteryStatsHelper, UserManager userManager, + int uid, int threshold) { + initBatteryStatsHelper(batteryStatsHelper, null /* bundle */, userManager); + final int dischargeAmount = batteryStatsHelper.getStats().getDischargeAmount( + BatteryStats.STATS_SINCE_CHARGED); + List batterySippers = batteryStatsHelper.getUsageList(); + final double hiddenAmount = removeHiddenBatterySippers(batterySippers); + + for (int i = 0, size = batterySippers.size(); i < size; i++) { + final BatterySipper batterySipper = batterySippers.get(i); + if (batterySipper.getUid() == uid) { + final int percent = (int) calculateBatteryPercent( + batterySipper.totalPowerMah, batteryStatsHelper.getTotalPower(), + hiddenAmount, + dischargeAmount); + return percent >= threshold; + } + } + + return false; + } } diff --git a/src/com/android/settings/fuelgauge/batterytip/AnomalyDetectionJobService.java b/src/com/android/settings/fuelgauge/batterytip/AnomalyDetectionJobService.java index 19fc0d58559..9d4f86f54c0 100644 --- a/src/com/android/settings/fuelgauge/batterytip/AnomalyDetectionJobService.java +++ b/src/com/android/settings/fuelgauge/batterytip/AnomalyDetectionJobService.java @@ -33,10 +33,12 @@ import android.content.Intent; import android.os.Bundle; import android.os.StatsDimensionsValue; import android.os.SystemPropertiesProto; +import android.os.UserManager; import android.provider.Settings; import android.support.annotation.VisibleForTesting; import android.util.Log; +import com.android.internal.os.BatteryStatsHelper; import com.android.settings.R; import com.android.settings.fuelgauge.BatteryUtils; import com.android.settingslib.utils.ThreadUtils; @@ -76,10 +78,14 @@ public class AnomalyDetectionJobService extends JobService { final BatteryTipPolicy policy = new BatteryTipPolicy(this); final BatteryUtils batteryUtils = BatteryUtils.getInstance(this); final ContentResolver contentResolver = getContentResolver(); + final BatteryStatsHelper batteryStatsHelper = new BatteryStatsHelper(this, + true /* collectBatteryBroadcast */); + final UserManager userManager = getSystemService(UserManager.class); for (JobWorkItem item = params.dequeueWork(); item != null; item = params.dequeueWork()) { - saveAnomalyToDatabase(batteryDatabaseManager, batteryUtils, policy, contentResolver, + saveAnomalyToDatabase(batteryStatsHelper, userManager, batteryDatabaseManager, + batteryUtils, policy, contentResolver, item.getIntent().getExtras()); } jobFinished(params, false /* wantsReschedule */); @@ -94,9 +100,9 @@ public class AnomalyDetectionJobService extends JobService { } @VisibleForTesting - void saveAnomalyToDatabase(BatteryDatabaseManager databaseManager, - BatteryUtils batteryUtils, BatteryTipPolicy policy, ContentResolver contentResolver, - Bundle bundle) { + void saveAnomalyToDatabase(BatteryStatsHelper batteryStatsHelper, UserManager userManager, + BatteryDatabaseManager databaseManager, BatteryUtils batteryUtils, + BatteryTipPolicy policy, ContentResolver contentResolver, Bundle bundle) { // The Example of intentDimsValue is: 35:{1:{1:{1:10013|}|}|} final StatsDimensionsValue intentDimsValue = bundle.getParcelable(StatsManager.EXTRA_STATS_DIMENSIONS_VALUE); @@ -116,7 +122,9 @@ public class AnomalyDetectionJobService extends JobService { if (anomalyType == StatsManagerConfig.AnomalyType.EXCESSIVE_BG) { // TODO(b/72385333): check battery percentage draining in batterystats - if (batteryUtils.isLegacyApp(packageName)) { + if (batteryUtils.isLegacyApp(packageName) && batteryUtils.isAppHeavilyUsed( + batteryStatsHelper, userManager, uid, + policy.excessiveBgDrainPercentage)) { Log.e(TAG, "Excessive detected uid=" + uid); batteryUtils.setForceAppStandby(uid, packageName, AppOpsManager.MODE_IGNORED); diff --git a/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicy.java b/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicy.java index 4a7b51d9ab0..fcdbb3a345f 100644 --- a/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicy.java +++ b/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicy.java @@ -44,6 +44,7 @@ public class BatteryTipPolicy { private static final String KEY_LOW_BATTERY_ENABLED = "low_battery_enabled"; private static final String KEY_LOW_BATTERY_HOUR = "low_battery_hour"; private static final String KEY_DATA_HISTORY_RETAIN_HOUR = "data_history_retain_hour"; + private static final String KEY_EXCESSIVE_BG_DRAIN_PERCENTAGE = "excessive_bg_drain_percentage"; /** * {@code true} if general battery tip is enabled @@ -151,6 +152,16 @@ public class BatteryTipPolicy { */ public final int dataHistoryRetainHour; + /** + * Battery drain percentage threshold for excessive background anomaly(i.e. 10%) + * + * This is an additional check for excessive background, to check whether battery drain + * for an app is larger than x% + * @see Settings.Global#BATTERY_TIP_CONSTANTS + * @see #KEY_EXCESSIVE_BG_DRAIN_PERCENTAGE + */ + public final int excessiveBgDrainPercentage; + private final KeyValueListParser mParser; public BatteryTipPolicy(Context context) { @@ -183,6 +194,7 @@ public class BatteryTipPolicy { lowBatteryEnabled = mParser.getBoolean(KEY_LOW_BATTERY_ENABLED, false); lowBatteryHour = mParser.getInt(KEY_LOW_BATTERY_HOUR, 16); dataHistoryRetainHour = mParser.getInt(KEY_DATA_HISTORY_RETAIN_HOUR, 72); + excessiveBgDrainPercentage = mParser.getInt(KEY_EXCESSIVE_BG_DRAIN_PERCENTAGE, 10); } } diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java index 6bc6ee71664..9d3fb7a436a 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java @@ -177,9 +177,9 @@ public class BatteryUtilsTest { mHighApplicationInfo.targetSdkVersion = Build.VERSION_CODES.O; mLowApplicationInfo.targetSdkVersion = Build.VERSION_CODES.L; - mNormalBatterySipper.drainType = BatterySipper.DrainType.APP; mNormalBatterySipper.totalPowerMah = TOTAL_BATTERY_USAGE; + doReturn(UID).when(mNormalBatterySipper).getUid(); mWifiBatterySipper.drainType = BatterySipper.DrainType.WIFI; mWifiBatterySipper.totalPowerMah = BATTERY_WIFI_USAGE; @@ -216,6 +216,10 @@ public class BatteryUtilsTest { mUsageList.add(mScreenBatterySipper); mUsageList.add(mCellBatterySipper); doReturn(mUsageList).when(mBatteryStatsHelper).getUsageList(); + doReturn(TOTAL_BATTERY_USAGE + BATTERY_SCREEN_USAGE).when( + mBatteryStatsHelper).getTotalPower(); + when(mBatteryStatsHelper.getStats().getDischargeAmount(anyInt())).thenReturn( + DISCHARGE_AMOUNT); } @Test @@ -547,4 +551,16 @@ public class BatteryUtilsTest { verify(mAppOpsManager).setMode(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, UID, HIGH_SDK_PACKAGE, AppOpsManager.MODE_IGNORED); } + + @Test + public void testIsAppHeavilyUsed_usageMoreThanThreshold_returnTrue() { + assertThat(mBatteryUtils.isAppHeavilyUsed(mBatteryStatsHelper, mUserManager, UID, + 10 /* threshold */ )).isTrue(); + } + + @Test + public void testIsAppHeavilyUsed_usageLessThanThreshold_returnFalse() { + assertThat(mBatteryUtils.isAppHeavilyUsed(mBatteryStatsHelper, mUserManager, UID, + DISCHARGE_AMOUNT /* threshold */ )).isFalse(); + } } diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicyTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicyTest.java index f36acee209a..abf04cd1440 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicyTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicyTest.java @@ -50,7 +50,8 @@ public class BatteryTipPolicyTest { + ",reduced_battery_percent=30" + ",low_battery_enabled=false" + ",low_battery_hour=10" - + ",data_history_retain_hour=24"; + + ",data_history_retain_hour=24" + + ",excessive_bg_drain_percentage=25"; private Context mContext; @Before @@ -78,6 +79,7 @@ public class BatteryTipPolicyTest { assertThat(batteryTipPolicy.lowBatteryEnabled).isFalse(); assertThat(batteryTipPolicy.lowBatteryHour).isEqualTo(10); assertThat(batteryTipPolicy.dataHistoryRetainHour).isEqualTo(24); + assertThat(batteryTipPolicy.excessiveBgDrainPercentage).isEqualTo(25); } @Test @@ -100,6 +102,6 @@ public class BatteryTipPolicyTest { assertThat(batteryTipPolicy.lowBatteryEnabled).isFalse(); assertThat(batteryTipPolicy.lowBatteryHour).isEqualTo(16); assertThat(batteryTipPolicy.dataHistoryRetainHour).isEqualTo(72); + assertThat(batteryTipPolicy.excessiveBgDrainPercentage).isEqualTo(10); } - }