Add the async task to compute diff usage data and load labels and icons.

Bug: 236101687
Test: make RunSettingsRoboTests
Change-Id: Ie24ea89fa6cfd351c73e64de40e2c9315867af9a
This commit is contained in:
Kuan Wang
2022-07-26 13:22:39 +08:00
parent a0d8b1fbc6
commit 0dc8d58de5
4 changed files with 1072 additions and 19 deletions

View File

@@ -266,8 +266,9 @@ public class BatteryChartPreferenceControllerV2 extends AbstractPreferenceContro
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap) {
Log.d(TAG, "setBatteryHistoryMap() " + (batteryHistoryMap == null ? "null"
: ("size=" + batteryHistoryMap.size())));
// TODO: implement the callback function.
final BatteryLevelData batteryLevelData =
DataProcessor.getBatteryLevelData(mContext, batteryHistoryMap);
DataProcessor.getBatteryLevelData(mContext, mHandler, batteryHistoryMap, null);
Log.d(TAG, "getBatteryLevelData: " + batteryLevelData);
if (batteryLevelData == null) {
mDailyViewModel = null;

View File

@@ -16,7 +16,9 @@
package com.android.settings.fuelgauge.batteryusage;
import java.util.ArrayList;
import androidx.annotation.NonNull;
import java.util.Collections;
import java.util.List;
/** Wraps the battery usage diff data for each entry used for battery usage app list. */
@@ -24,10 +26,24 @@ public class BatteryDiffData {
private final List<BatteryDiffEntry> mAppEntries;
private final List<BatteryDiffEntry> mSystemEntries;
/** Constructor for the diff entries which already have totalConsumePower value. */
public BatteryDiffData(
List<BatteryDiffEntry> appDiffEntries, List<BatteryDiffEntry> systemDiffEntries) {
mAppEntries = appDiffEntries == null ? new ArrayList<>() : appDiffEntries;
mSystemEntries = systemDiffEntries == null ? new ArrayList<>() : systemDiffEntries;
@NonNull List<BatteryDiffEntry> appDiffEntries,
@NonNull List<BatteryDiffEntry> systemDiffEntries) {
mAppEntries = appDiffEntries;
mSystemEntries = systemDiffEntries;
sortEntries();
}
/** Constructor for the diff entries which have not set totalConsumePower value. */
public BatteryDiffData(
@NonNull List<BatteryDiffEntry> appDiffEntries,
@NonNull List<BatteryDiffEntry> systemDiffEntries,
final double totalConsumePower) {
mAppEntries = appDiffEntries;
mSystemEntries = systemDiffEntries;
setTotalConsumePowerForAllEntries(totalConsumePower);
sortEntries();
}
public List<BatteryDiffEntry> getAppDiffEntryList() {
@@ -38,9 +54,15 @@ public class BatteryDiffData {
return mSystemEntries;
}
/** Sets total consume power for each entry. */
public void setTotalConsumePowerForAllEntries(double totalConsumePower) {
// Sets total consume power for each entry.
private void setTotalConsumePowerForAllEntries(final double totalConsumePower) {
mAppEntries.forEach(diffEntry -> diffEntry.setTotalConsumePower(totalConsumePower));
mSystemEntries.forEach(diffEntry -> diffEntry.setTotalConsumePower(totalConsumePower));
}
// Sorts entries based on consumed percentage.
private void sortEntries() {
Collections.sort(mAppEntries, BatteryDiffEntry.COMPARATOR);
Collections.sort(mSystemEntries, BatteryDiffEntry.COMPARATOR);
}
}

View File

@@ -18,21 +18,36 @@ package com.android.settings.fuelgauge.batteryusage;
import static com.android.settings.fuelgauge.batteryusage.ConvertUtils.utcToLocalTime;
import android.content.ContentValues;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.Looper;
import android.os.UserHandle;
import android.os.UserManager;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.ArraySet;
import android.util.Log;
import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
import com.android.settings.Utils;
import com.android.settings.fuelgauge.BatteryUtils;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.fuelgauge.BatteryStatus;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* A utility class to process data loaded from database and make the data easy to use for battery
@@ -43,10 +58,27 @@ public final class DataProcessor {
private static final String TAG = "DataProcessor";
private static final int MIN_DAILY_DATA_SIZE = 2;
private static final int MIN_TIMESTAMP_DATA_SIZE = 2;
// Maximum total time value for each hourly slot cumulative data at most 2 hours.
private static final float TOTAL_HOURLY_TIME_THRESHOLD = DateUtils.HOUR_IN_MILLIS * 2;
private static final Map<String, BatteryHistEntry> EMPTY_BATTERY_MAP = new HashMap<>();
private static final BatteryHistEntry EMPTY_BATTERY_HIST_ENTRY =
new BatteryHistEntry(new ContentValues());
@VisibleForTesting
static final double PERCENTAGE_OF_TOTAL_THRESHOLD = 1f;
@VisibleForTesting
static final int SELECTED_INDEX_ALL = BatteryChartViewModel.SELECTED_INDEX_ALL;
/** A fake package name to represent no BatteryEntry data. */
public static final String FAKE_PACKAGE_NAME = "fake_package";
/** A callback listener when battery usage loading async task is executed. */
public interface UsageMapAsyncResponse {
/** The callback function when batteryUsageMap is loaded. */
void onBatteryUsageMapLoaded(
Map<Integer, Map<Integer, BatteryDiffData>> batteryUsageMap);
}
private DataProcessor() {
}
@@ -58,11 +90,14 @@ public final class DataProcessor {
@Nullable
public static BatteryLevelData getBatteryLevelData(
Context context,
@Nullable final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap) {
@Nullable Handler handler,
@Nullable final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap,
final UsageMapAsyncResponse asyncResponseDelegate) {
if (batteryHistoryMap == null || batteryHistoryMap.isEmpty()) {
Log.d(TAG, "getBatteryLevelData() returns null");
return null;
}
handler = handler != null ? handler : new Handler(Looper.getMainLooper());
// Process raw history map data into hourly timestamps.
final Map<Long, Map<String, BatteryHistEntry>> processedBatteryHistoryMap =
getHistoryMapWithExpectedTimestamps(context, batteryHistoryMap);
@@ -70,11 +105,33 @@ public final class DataProcessor {
final BatteryLevelData batteryLevelData =
getLevelDataThroughProcessedHistoryMap(context, processedBatteryHistoryMap);
//TODO: Add the async task to compute diff usage data and load labels and icons.
// Start the async task to compute diff usage data and load labels and icons.
if (batteryLevelData != null) {
new ComputeUsageMapAndLoadItemsTask(
context,
handler,
asyncResponseDelegate,
batteryLevelData.getHourlyBatteryLevelsPerDay(),
processedBatteryHistoryMap).execute();
}
return batteryLevelData;
}
/**
* @return Returns whether the target is in the CharSequence array.
*/
public static boolean contains(String target, CharSequence[] packageNames) {
if (target != null && packageNames != null) {
for (CharSequence packageName : packageNames) {
if (TextUtils.equals(target, packageName)) {
return true;
}
}
}
return false;
}
/**
* @return Returns the processed history map which has interpolated to every hour data.
* The start and end timestamp must be the even hours.
@@ -187,7 +244,7 @@ public final class DataProcessor {
@VisibleForTesting
static boolean isFromFullCharge(@Nullable final Map<String, BatteryHistEntry> entryList) {
if (entryList == null) {
Log.d(TAG, "entryList is nul in isFromFullCharge()");
Log.d(TAG, "entryList is null in isFromFullCharge()");
return false;
}
final List<String> entryKeys = new ArrayList<>(entryList.keySet());
@@ -205,14 +262,14 @@ public final class DataProcessor {
static long[] findNearestTimestamp(final List<Long> timestamps, final long target) {
final long[] results = new long[] {Long.MIN_VALUE, Long.MAX_VALUE};
// Searches the nearest lower and upper timestamp value.
for (long timestamp : timestamps) {
timestamps.forEach(timestamp -> {
if (timestamp <= target && timestamp > results[0]) {
results[0] = timestamp;
}
if (timestamp >= target && timestamp < results[1]) {
results[1] = timestamp;
}
}
});
// Uses zero value to represent invalid searching result.
results[0] = results[0] == Long.MIN_VALUE ? 0 : results[0];
results[1] = results[1] == Long.MAX_VALUE ? 0 : results[1];
@@ -234,6 +291,40 @@ public final class DataProcessor {
return nextDayCalendar.getTimeInMillis();
}
/**
* @return Returns the indexed battery usage data for each corresponding time slot.
*
* There could be 2 cases of the returned value:
* 1) null: empty or invalid data.
* 2) non-null: must be a 2d map and composed by 3 parts:
* 1 - [SELECTED_INDEX_ALL][SELECTED_INDEX_ALL]
* 2 - [0][SELECTED_INDEX_ALL] ~ [maxDailyIndex][SELECTED_INDEX_ALL]
* 3 - [0][0] ~ [maxDailyIndex][maxHourlyIndex]
*/
@VisibleForTesting
@Nullable
static Map<Integer, Map<Integer, BatteryDiffData>> getBatteryUsageMap(
final Context context,
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay,
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap) {
if (batteryHistoryMap.isEmpty()) {
return null;
}
final Map<Integer, Map<Integer, BatteryDiffData>> resultMap = new HashMap<>();
// Insert diff data from [0][0] to [maxDailyIndex][maxHourlyIndex].
insertHourlyUsageDiffData(
context, hourlyBatteryLevelsPerDay, batteryHistoryMap, resultMap);
// Insert diff data from [0][SELECTED_INDEX_ALL] to [maxDailyIndex][SELECTED_INDEX_ALL].
insertDailyUsageDiffData(hourlyBatteryLevelsPerDay, resultMap);
// Insert diff data [SELECTED_INDEX_ALL][SELECTED_INDEX_ALL].
insertAllUsageDiffData(resultMap);
purgeLowPercentageAndFakeData(context, resultMap);
if (!isUsageMapValid(resultMap, hourlyBatteryLevelsPerDay)) {
return null;
}
return resultMap;
}
/**
* Interpolates history map based on expected timestamp slots and processes the corner case when
* the expected start timestamp is earlier than what we have.
@@ -458,11 +549,454 @@ public final class DataProcessor {
return Math.round(batteryLevelCounter / entryMap.size());
}
private static void log(Context context, String content, long timestamp,
BatteryHistEntry entry) {
private static void insertHourlyUsageDiffData(
Context context,
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay,
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap,
final Map<Integer, Map<Integer, BatteryDiffData>> resultMap) {
final int currentUserId = context.getUserId();
final UserHandle userHandle =
Utils.getManagedProfile(context.getSystemService(UserManager.class));
final int workProfileUserId =
userHandle != null ? userHandle.getIdentifier() : Integer.MIN_VALUE;
// Each time slot usage diff data =
// Math.abs(timestamp[i+2] data - timestamp[i+1] data) +
// Math.abs(timestamp[i+1] data - timestamp[i] data);
// since we want to aggregate every two hours data into a single time slot.
for (int dailyIndex = 0; dailyIndex < hourlyBatteryLevelsPerDay.size(); dailyIndex++) {
final Map<Integer, BatteryDiffData> dailyDiffMap = new HashMap<>();
resultMap.put(dailyIndex, dailyDiffMap);
if (hourlyBatteryLevelsPerDay.get(dailyIndex) == null) {
continue;
}
final List<Long> timestamps = hourlyBatteryLevelsPerDay.get(dailyIndex).getTimestamps();
for (int hourlyIndex = 0; hourlyIndex < timestamps.size() - 1; hourlyIndex++) {
final BatteryDiffData hourlyBatteryDiffData =
insertHourlyUsageDiffDataPerSlot(
context,
currentUserId,
workProfileUserId,
hourlyIndex,
timestamps,
batteryHistoryMap);
dailyDiffMap.put(hourlyIndex, hourlyBatteryDiffData);
}
}
}
private static void insertDailyUsageDiffData(
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay,
final Map<Integer, Map<Integer, BatteryDiffData>> resultMap) {
for (int index = 0; index < hourlyBatteryLevelsPerDay.size(); index++) {
Map<Integer, BatteryDiffData> dailyUsageMap = resultMap.get(index);
if (dailyUsageMap == null) {
dailyUsageMap = new HashMap<>();
resultMap.put(index, dailyUsageMap);
}
dailyUsageMap.put(
SELECTED_INDEX_ALL,
getAccumulatedUsageDiffData(dailyUsageMap.values()));
}
}
private static void insertAllUsageDiffData(
final Map<Integer, Map<Integer, BatteryDiffData>> resultMap) {
final List<BatteryDiffData> diffDataList = new ArrayList<>();
resultMap.keySet().forEach(
key -> diffDataList.add(resultMap.get(key).get(SELECTED_INDEX_ALL)));
final Map<Integer, BatteryDiffData> allUsageMap = new HashMap<>();
allUsageMap.put(SELECTED_INDEX_ALL, getAccumulatedUsageDiffData(diffDataList));
resultMap.put(SELECTED_INDEX_ALL, allUsageMap);
}
@Nullable
private static BatteryDiffData insertHourlyUsageDiffDataPerSlot(
Context context,
final int currentUserId,
final int workProfileUserId,
final int currentIndex,
final List<Long> timestamps,
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap) {
final List<BatteryDiffEntry> appEntries = new ArrayList<>();
final List<BatteryDiffEntry> systemEntries = new ArrayList<>();
final Long currentTimestamp = timestamps.get(currentIndex);
final Long nextTimestamp = currentTimestamp + DateUtils.HOUR_IN_MILLIS;
final Long nextTwoTimestamp = nextTimestamp + DateUtils.HOUR_IN_MILLIS;
// Fetches BatteryHistEntry data from corresponding time slot.
final Map<String, BatteryHistEntry> currentBatteryHistMap =
batteryHistoryMap.getOrDefault(currentTimestamp, EMPTY_BATTERY_MAP);
final Map<String, BatteryHistEntry> nextBatteryHistMap =
batteryHistoryMap.getOrDefault(nextTimestamp, EMPTY_BATTERY_MAP);
final Map<String, BatteryHistEntry> nextTwoBatteryHistMap =
batteryHistoryMap.getOrDefault(nextTwoTimestamp, EMPTY_BATTERY_MAP);
// We should not get the empty list since we have at least one fake data to record
// the battery level and status in each time slot, the empty list is used to
// represent there is no enough data to apply interpolation arithmetic.
if (currentBatteryHistMap.isEmpty()
|| nextBatteryHistMap.isEmpty()
|| nextTwoBatteryHistMap.isEmpty()) {
return null;
}
// Collects all keys in these three time slot records as all populations.
final Set<String> allBatteryHistEntryKeys = new ArraySet<>();
allBatteryHistEntryKeys.addAll(currentBatteryHistMap.keySet());
allBatteryHistEntryKeys.addAll(nextBatteryHistMap.keySet());
allBatteryHistEntryKeys.addAll(nextTwoBatteryHistMap.keySet());
double totalConsumePower = 0.0;
double consumePowerFromOtherUsers = 0f;
// Calculates all packages diff usage data in a specific time slot.
for (String key : allBatteryHistEntryKeys) {
final BatteryHistEntry currentEntry =
currentBatteryHistMap.getOrDefault(key, EMPTY_BATTERY_HIST_ENTRY);
final BatteryHistEntry nextEntry =
nextBatteryHistMap.getOrDefault(key, EMPTY_BATTERY_HIST_ENTRY);
final BatteryHistEntry nextTwoEntry =
nextTwoBatteryHistMap.getOrDefault(key, EMPTY_BATTERY_HIST_ENTRY);
// Cumulative values is a specific time slot for a specific app.
long foregroundUsageTimeInMs =
getDiffValue(
currentEntry.mForegroundUsageTimeInMs,
nextEntry.mForegroundUsageTimeInMs,
nextTwoEntry.mForegroundUsageTimeInMs);
long backgroundUsageTimeInMs =
getDiffValue(
currentEntry.mBackgroundUsageTimeInMs,
nextEntry.mBackgroundUsageTimeInMs,
nextTwoEntry.mBackgroundUsageTimeInMs);
double consumePower =
getDiffValue(
currentEntry.mConsumePower,
nextEntry.mConsumePower,
nextTwoEntry.mConsumePower);
// Excludes entry since we don't have enough data to calculate.
if (foregroundUsageTimeInMs == 0
&& backgroundUsageTimeInMs == 0
&& consumePower == 0) {
continue;
}
final BatteryHistEntry selectedBatteryEntry =
selectBatteryHistEntry(currentEntry, nextEntry, nextTwoEntry);
if (selectedBatteryEntry == null) {
continue;
}
// Forces refine the cumulative value since it may introduce deviation error since we
// will apply the interpolation arithmetic.
final float totalUsageTimeInMs =
foregroundUsageTimeInMs + backgroundUsageTimeInMs;
if (totalUsageTimeInMs > TOTAL_HOURLY_TIME_THRESHOLD) {
final float ratio = TOTAL_HOURLY_TIME_THRESHOLD / totalUsageTimeInMs;
if (DEBUG) {
Log.w(TAG, String.format("abnormal usage time %d|%d for:\n%s",
Duration.ofMillis(foregroundUsageTimeInMs).getSeconds(),
Duration.ofMillis(backgroundUsageTimeInMs).getSeconds(),
currentEntry));
}
foregroundUsageTimeInMs =
Math.round(foregroundUsageTimeInMs * ratio);
backgroundUsageTimeInMs =
Math.round(backgroundUsageTimeInMs * ratio);
consumePower = consumePower * ratio;
}
totalConsumePower += consumePower;
final boolean isFromOtherUsers = isConsumedFromOtherUsers(
currentUserId, workProfileUserId, selectedBatteryEntry);
if (isFromOtherUsers) {
consumePowerFromOtherUsers += consumePower;
} else {
final BatteryDiffEntry currentBatteryDiffEntry = new BatteryDiffEntry(
context,
foregroundUsageTimeInMs,
backgroundUsageTimeInMs,
consumePower,
selectedBatteryEntry);
if (currentBatteryDiffEntry.isSystemEntry()) {
systemEntries.add(currentBatteryDiffEntry);
} else {
appEntries.add(currentBatteryDiffEntry);
}
}
}
if (consumePowerFromOtherUsers != 0) {
systemEntries.add(createOtherUsersEntry(context, consumePowerFromOtherUsers));
}
// If there is no data, return null instead of empty item.
if (appEntries.isEmpty() && systemEntries.isEmpty()) {
return null;
}
final BatteryDiffData resultDiffData =
new BatteryDiffData(appEntries, systemEntries, totalConsumePower);
return resultDiffData;
}
private static boolean isConsumedFromOtherUsers(
final int currentUserId,
final int workProfileUserId,
final BatteryHistEntry batteryHistEntry) {
return batteryHistEntry.mConsumerType == ConvertUtils.CONSUMER_TYPE_UID_BATTERY
&& batteryHistEntry.mUserId != currentUserId
&& batteryHistEntry.mUserId != workProfileUserId;
}
@Nullable
private static BatteryDiffData getAccumulatedUsageDiffData(
final Collection<BatteryDiffData> diffEntryListData) {
double totalConsumePower = 0f;
final Map<String, BatteryDiffEntry> diffEntryMap = new HashMap<>();
final List<BatteryDiffEntry> appEntries = new ArrayList<>();
final List<BatteryDiffEntry> systemEntries = new ArrayList<>();
for (BatteryDiffData diffEntryList : diffEntryListData) {
if (diffEntryList == null) {
continue;
}
for (BatteryDiffEntry entry : diffEntryList.getAppDiffEntryList()) {
computeUsageDiffDataPerEntry(entry, diffEntryMap);
totalConsumePower += entry.mConsumePower;
}
for (BatteryDiffEntry entry : diffEntryList.getSystemDiffEntryList()) {
computeUsageDiffDataPerEntry(entry, diffEntryMap);
totalConsumePower += entry.mConsumePower;
}
}
final Collection<BatteryDiffEntry> diffEntryList = diffEntryMap.values();
for (BatteryDiffEntry entry : diffEntryList) {
// Sets total daily consume power data into all BatteryDiffEntry.
entry.setTotalConsumePower(totalConsumePower);
if (entry.isSystemEntry()) {
systemEntries.add(entry);
} else {
appEntries.add(entry);
}
}
return diffEntryList.isEmpty() ? null : new BatteryDiffData(appEntries, systemEntries);
}
private static void computeUsageDiffDataPerEntry(
final BatteryDiffEntry entry,
final Map<String, BatteryDiffEntry> diffEntryMap) {
final String key = entry.mBatteryHistEntry.getKey();
final BatteryDiffEntry oldBatteryDiffEntry = diffEntryMap.get(key);
// Creates new BatteryDiffEntry if we don't have it.
if (oldBatteryDiffEntry == null) {
diffEntryMap.put(key, entry.clone());
} else {
// Sums up some field data into the existing one.
oldBatteryDiffEntry.mForegroundUsageTimeInMs +=
entry.mForegroundUsageTimeInMs;
oldBatteryDiffEntry.mBackgroundUsageTimeInMs +=
entry.mBackgroundUsageTimeInMs;
oldBatteryDiffEntry.mConsumePower += entry.mConsumePower;
}
}
// Removes low percentage data and fake usage data, which will be zero value.
private static void purgeLowPercentageAndFakeData(
final Context context,
final Map<Integer, Map<Integer, BatteryDiffData>> resultMap) {
final Set<CharSequence> backgroundUsageTimeHideList =
FeatureFactory.getFactory(context)
.getPowerUsageFeatureProvider(context)
.getHideBackgroundUsageTimeSet(context);
final CharSequence[] notAllowShowEntryPackages =
FeatureFactory.getFactory(context)
.getPowerUsageFeatureProvider(context)
.getHideApplicationEntries(context);
resultMap.keySet().forEach(dailyKey -> {
final Map<Integer, BatteryDiffData> dailyUsageMap = resultMap.get(dailyKey);
dailyUsageMap.values().forEach(diffEntryLists -> {
if (diffEntryLists == null) {
return;
}
purgeLowPercentageAndFakeData(
diffEntryLists.getAppDiffEntryList(), backgroundUsageTimeHideList,
notAllowShowEntryPackages);
purgeLowPercentageAndFakeData(
diffEntryLists.getSystemDiffEntryList(), backgroundUsageTimeHideList,
notAllowShowEntryPackages);
});
});
}
private static void purgeLowPercentageAndFakeData(
final List<BatteryDiffEntry> entries,
final Set<CharSequence> backgroundUsageTimeHideList,
final CharSequence[] notAllowShowEntryPackages) {
final Iterator<BatteryDiffEntry> iterator = entries.iterator();
while (iterator.hasNext()) {
final BatteryDiffEntry entry = iterator.next();
final String packageName = entry.getPackageName();
if (entry.getPercentOfTotal() < PERCENTAGE_OF_TOTAL_THRESHOLD
|| FAKE_PACKAGE_NAME.equals(packageName)
|| contains(packageName, notAllowShowEntryPackages)) {
iterator.remove();
}
if (packageName != null
&& !backgroundUsageTimeHideList.isEmpty()
&& contains(packageName, backgroundUsageTimeHideList)) {
entry.mBackgroundUsageTimeInMs = 0;
}
}
}
private static boolean isUsageMapValid(
final Map<Integer, Map<Integer, BatteryDiffData>> batteryUsageMap,
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay) {
if (batteryUsageMap.get(SELECTED_INDEX_ALL) == null
|| batteryUsageMap.get(SELECTED_INDEX_ALL).get(SELECTED_INDEX_ALL) == null) {
Log.e(TAG, "no [SELECTED_INDEX_ALL][SELECTED_INDEX_ALL] in batteryUsageMap");
return false;
}
for (int dailyIndex = 0; dailyIndex < hourlyBatteryLevelsPerDay.size(); dailyIndex++) {
if (batteryUsageMap.get(dailyIndex) == null
|| !batteryUsageMap.get(dailyIndex).containsKey(SELECTED_INDEX_ALL)) {
Log.e(TAG, "no [" + dailyIndex + "][SELECTED_INDEX_ALL] in batteryUsageMap, "
+ "daily size is: " + hourlyBatteryLevelsPerDay.size());
return false;
}
if (hourlyBatteryLevelsPerDay.get(dailyIndex) == null) {
continue;
}
final List<Long> timestamps = hourlyBatteryLevelsPerDay.get(dailyIndex).getTimestamps();
// Length of hourly usage map should be the length of hourly level data - 1.
for (int hourlyIndex = 0; hourlyIndex < timestamps.size() - 1; hourlyIndex++) {
if (!batteryUsageMap.get(dailyIndex).containsKey(hourlyIndex)) {
Log.e(TAG, "no [" + dailyIndex + "][" + hourlyIndex + "] in batteryUsageMap, "
+ "hourly size is: " + (timestamps.size() - 1));
return false;
}
}
}
return true;
}
private static boolean contains(String target, Set<CharSequence> packageNames) {
if (target != null && packageNames != null) {
for (CharSequence packageName : packageNames) {
if (TextUtils.equals(target, packageName)) {
return true;
}
}
}
return false;
}
private static long getDiffValue(long v1, long v2, long v3) {
return (v2 > v1 ? v2 - v1 : 0) + (v3 > v2 ? v3 - v2 : 0);
}
private static double getDiffValue(double v1, double v2, double v3) {
return (v2 > v1 ? v2 - v1 : 0) + (v3 > v2 ? v3 - v2 : 0);
}
@Nullable
private static BatteryHistEntry selectBatteryHistEntry(
final BatteryHistEntry... batteryHistEntries) {
for (BatteryHistEntry entry : batteryHistEntries) {
if (entry != null && entry != EMPTY_BATTERY_HIST_ENTRY) {
return entry;
}
}
return null;
}
private static BatteryDiffEntry createOtherUsersEntry(
Context context, final double consumePower) {
final ContentValues values = new ContentValues();
values.put(BatteryHistEntry.KEY_UID, BatteryUtils.UID_OTHER_USERS);
values.put(BatteryHistEntry.KEY_USER_ID, BatteryUtils.UID_OTHER_USERS);
values.put(BatteryHistEntry.KEY_CONSUMER_TYPE, ConvertUtils.CONSUMER_TYPE_UID_BATTERY);
// We will show the percentage for the "other users" item only, the aggregated
// running time information is useless for users to identify individual apps.
final BatteryDiffEntry batteryDiffEntry = new BatteryDiffEntry(
context,
/*foregroundUsageTimeInMs=*/ 0,
/*backgroundUsageTimeInMs=*/ 0,
consumePower,
new BatteryHistEntry(values));
return batteryDiffEntry;
}
private static void log(Context context, final String content, final long timestamp,
final BatteryHistEntry entry) {
if (DEBUG) {
Log.d(TAG, String.format(entry != null ? "%s %s:\n%s" : "%s %s:%s",
utcToLocalTime(context, timestamp), content, entry));
}
}
// Compute diff map and loads all items (icon and label) in the background.
private static final class ComputeUsageMapAndLoadItemsTask
extends AsyncTask<Void, Void, Map<Integer, Map<Integer, BatteryDiffData>>> {
private Context mApplicationContext;
private Handler mHandler;
private UsageMapAsyncResponse mAsyncResponseDelegate;
private List<BatteryLevelData.PeriodBatteryLevelData> mHourlyBatteryLevelsPerDay;
private Map<Long, Map<String, BatteryHistEntry>> mBatteryHistoryMap;
private ComputeUsageMapAndLoadItemsTask(
Context context,
Handler handler,
final UsageMapAsyncResponse asyncResponseDelegate,
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay,
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap) {
mApplicationContext = context.getApplicationContext();
mHandler = handler;
mAsyncResponseDelegate = asyncResponseDelegate;
mHourlyBatteryLevelsPerDay = hourlyBatteryLevelsPerDay;
mBatteryHistoryMap = batteryHistoryMap;
}
@Override
protected Map<Integer, Map<Integer, BatteryDiffData>> doInBackground(Void... voids) {
if (mApplicationContext == null
|| mHandler == null
|| mAsyncResponseDelegate == null
|| mBatteryHistoryMap == null
|| mHourlyBatteryLevelsPerDay == null) {
Log.e(TAG, "invalid input for ComputeUsageMapAndLoadItemsTask()");
return null;
}
final long startTime = System.currentTimeMillis();
final Map<Integer, Map<Integer, BatteryDiffData>> batteryUsageMap =
getBatteryUsageMap(
mApplicationContext, mHourlyBatteryLevelsPerDay, mBatteryHistoryMap);
if (batteryUsageMap != null) {
// Pre-loads each BatteryDiffEntry relative icon and label for all slots.
final BatteryDiffData batteryUsageMapForAll =
batteryUsageMap.get(SELECTED_INDEX_ALL).get(SELECTED_INDEX_ALL);
if (batteryUsageMapForAll != null) {
batteryUsageMapForAll.getAppDiffEntryList().forEach(
entry -> entry.loadLabelAndIcon());
batteryUsageMapForAll.getSystemDiffEntryList().forEach(
entry -> entry.loadLabelAndIcon());
}
}
Log.d(TAG, String.format("execute ComputeUsageMapAndLoadItemsTask in %d/ms",
(System.currentTimeMillis() - startTime)));
return batteryUsageMap;
}
@Override
protected void onPostExecute(
final Map<Integer, Map<Integer, BatteryDiffData>> batteryUsageMap) {
mApplicationContext = null;
mHourlyBatteryLevelsPerDay = null;
mBatteryHistoryMap = null;
// Post results back to main thread to refresh UI.
if (mHandler != null && mAsyncResponseDelegate != null) {
mHandler.post(() -> {
mAsyncResponseDelegate.onBatteryUsageMapLoaded(batteryUsageMap);
});
}
}
}
}

View File

@@ -19,11 +19,16 @@ package com.android.settings.fuelgauge.batteryusage;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.content.ContentValues;
import android.content.Context;
import android.text.format.DateUtils;
import com.android.settings.fuelgauge.BatteryUtils;
import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
import com.android.settings.testutils.FakeFeatureFactory;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -35,6 +40,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
@@ -45,18 +51,30 @@ public class DataProcessorTest {
private Context mContext;
private FakeFeatureFactory mFeatureFactory;
private PowerUsageFeatureProvider mPowerUsageFeatureProvider;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
TimeZone.setDefault(TimeZone.getTimeZone("GMT+8"));
mContext = spy(RuntimeEnvironment.application);
mFeatureFactory = FakeFeatureFactory.setupForTest();
mPowerUsageFeatureProvider = mFeatureFactory.powerUsageFeatureProvider;
}
@Test
public void getBatteryLevelData_emptyHistoryMap_returnNull() {
assertThat(DataProcessor.getBatteryLevelData(mContext, null)).isNull();
assertThat(DataProcessor.getBatteryLevelData(mContext, new HashMap<>())).isNull();
assertThat(DataProcessor.getBatteryLevelData(
mContext,
/*handler=*/ null,
/*batteryHistoryMap=*/ null,
/*asyncResponseDelegate=*/ null))
.isNull();
assertThat(DataProcessor.getBatteryLevelData(
mContext, /*handler=*/ null, new HashMap<>(), /*asyncResponseDelegate=*/ null))
.isNull();
}
@Test
@@ -67,7 +85,9 @@ public class DataProcessorTest {
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap =
createHistoryMap(timestamps, levels);
assertThat(DataProcessor.getBatteryLevelData(mContext, batteryHistoryMap)).isNull();
assertThat(DataProcessor.getBatteryLevelData(
mContext, /*handler=*/ null, batteryHistoryMap, /*asyncResponseDelegate=*/ null))
.isNull();
}
@Test
@@ -79,7 +99,11 @@ public class DataProcessorTest {
createHistoryMap(timestamps, levels);
final BatteryLevelData resultData =
DataProcessor.getBatteryLevelData(mContext, batteryHistoryMap);
DataProcessor.getBatteryLevelData(
mContext,
/*handler=*/ null,
batteryHistoryMap,
/*asyncResponseDelegate=*/ null);
final List<Long> expectedDailyTimestamps = List.of(timestamps[0], timestamps[2]);
final List<Integer> expectedDailyLevels = List.of(levels[0], levels[2]);
@@ -377,6 +401,450 @@ public class DataProcessorTest {
.isEqualTo(1640966400000L);
}
@Test
public void getBatteryUsageMap_emptyHistoryMap_returnNull() {
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay =
new ArrayList<>();
hourlyBatteryLevelsPerDay.add(
new BatteryLevelData.PeriodBatteryLevelData(new ArrayList<>(), new ArrayList<>()));
assertThat(DataProcessor.getBatteryUsageMap(
mContext, hourlyBatteryLevelsPerDay, new HashMap<>())).isNull();
}
@Test
public void getBatteryUsageMap_emptyHourlyData_returnNull() {
final long[] timestamps = {1000000L, 2000000L};
final int[] levels = {100, 99};
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap =
createHistoryMap(timestamps, levels);
assertThat(DataProcessor.getBatteryUsageMap(
mContext, new ArrayList<>(), batteryHistoryMap)).isNull();
}
@Test
public void getBatteryUsageMap_returnsExpectedResult() {
final long[] batteryHistoryKeys = new long[]{
1641045600000L, // 2022-01-01 22:00:00
1641049200000L, // 2022-01-01 23:00:00
1641052800000L, // 2022-01-02 00:00:00
1641056400000L, // 2022-01-02 01:00:00
1641060000000L, // 2022-01-02 02:00:00
};
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap = new HashMap<>();
final int currentUserId = mContext.getUserId();
final BatteryHistEntry fakeEntry = createBatteryHistEntry(
ConvertUtils.FAKE_PACKAGE_NAME, "fake_label", /*consumePower=*/ 0, /*uid=*/ 0L,
currentUserId, ConvertUtils.CONSUMER_TYPE_UID_BATTERY,
/*foregroundUsageTimeInMs=*/ 0L, /*backgroundUsageTimeInMs=*/ 0L);
// Adds the index = 0 data.
Map<String, BatteryHistEntry> entryMap = new HashMap<>();
BatteryHistEntry entry = createBatteryHistEntry(
"package1", "label1", /*consumePower=*/ 5.0, /*uid=*/ 1L, currentUserId,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 10L,
/*backgroundUsageTimeInMs=*/ 20L);
entryMap.put(entry.getKey(), entry);
entryMap.put(fakeEntry.getKey(), fakeEntry);
batteryHistoryMap.put(batteryHistoryKeys[0], entryMap);
// Adds the index = 1 data.
entryMap = new HashMap<>();
entryMap.put(fakeEntry.getKey(), fakeEntry);
batteryHistoryMap.put(batteryHistoryKeys[1], entryMap);
// Adds the index = 2 data.
entryMap = new HashMap<>();
entry = createBatteryHistEntry(
"package2", "label2", /*consumePower=*/ 20.0, /*uid=*/ 2L, currentUserId,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 15L,
25L);
entryMap.put(entry.getKey(), entry);
entryMap.put(fakeEntry.getKey(), fakeEntry);
batteryHistoryMap.put(batteryHistoryKeys[2], entryMap);
// Adds the index = 3 data.
entryMap = new HashMap<>();
entry = createBatteryHistEntry(
"package2", "label2", /*consumePower=*/ 40.0, /*uid=*/ 2L, currentUserId,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 25L,
/*backgroundUsageTimeInMs=*/ 35L);
entryMap.put(entry.getKey(), entry);
entry = createBatteryHistEntry(
"package2", "label2", /*consumePower=*/ 10.0, /*uid=*/ 3L, currentUserId,
ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY, /*foregroundUsageTimeInMs=*/ 40L,
/*backgroundUsageTimeInMs=*/ 50L);
entryMap.put(entry.getKey(), entry);
entry = createBatteryHistEntry(
"package3", "label3", /*consumePower=*/ 15.0, /*uid=*/ 4L, currentUserId,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 5L,
/*backgroundUsageTimeInMs=*/ 5L);
entryMap.put(entry.getKey(), entry);
entryMap.put(fakeEntry.getKey(), fakeEntry);
batteryHistoryMap.put(batteryHistoryKeys[3], entryMap);
// Adds the index = 4 data.
entryMap = new HashMap<>();
entry = createBatteryHistEntry(
"package2", "label2", /*consumePower=*/ 40.0, /*uid=*/ 2L, currentUserId,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 30L,
/*backgroundUsageTimeInMs=*/ 40L);
entryMap.put(entry.getKey(), entry);
entry = createBatteryHistEntry(
"package2", "label2", /*consumePower=*/ 20.0, /*uid=*/ 3L, currentUserId,
ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY, /*foregroundUsageTimeInMs=*/ 50L,
/*backgroundUsageTimeInMs=*/ 60L);
entryMap.put(entry.getKey(), entry);
entry = createBatteryHistEntry(
"package3", "label3", /*consumePower=*/ 40.0, /*uid=*/ 4L, currentUserId,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 5L,
/*backgroundUsageTimeInMs=*/ 5L);
entryMap.put(entry.getKey(), entry);
entryMap.put(fakeEntry.getKey(), fakeEntry);
batteryHistoryMap.put(batteryHistoryKeys[4], entryMap);
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay =
new ArrayList<>();
// Adds the day 1 data.
List<Long> timestamps =
List.of(batteryHistoryKeys[0], batteryHistoryKeys[2]);
final List<Integer> levels = List.of(100, 100);
hourlyBatteryLevelsPerDay.add(
new BatteryLevelData.PeriodBatteryLevelData(timestamps, levels));
// Adds the day 2 data.
timestamps = List.of(batteryHistoryKeys[2], batteryHistoryKeys[4]);
hourlyBatteryLevelsPerDay.add(
new BatteryLevelData.PeriodBatteryLevelData(timestamps, levels));
final Map<Integer, Map<Integer, BatteryDiffData>> resultMap =
DataProcessor.getBatteryUsageMap(
mContext, hourlyBatteryLevelsPerDay, batteryHistoryMap);
BatteryDiffData resultDiffData =
resultMap
.get(DataProcessor.SELECTED_INDEX_ALL)
.get(DataProcessor.SELECTED_INDEX_ALL);
assertBatteryDiffEntry(
resultDiffData.getAppDiffEntryList().get(0), currentUserId, /*uid=*/ 2L,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*consumePercentage=*/ 40.0,
/*foregroundUsageTimeInMs=*/ 30, /*backgroundUsageTimeInMs=*/ 40);
assertBatteryDiffEntry(
resultDiffData.getAppDiffEntryList().get(1), currentUserId, /*uid=*/ 4L,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*consumePercentage=*/ 40.0,
/*foregroundUsageTimeInMs=*/ 5, /*backgroundUsageTimeInMs=*/ 5);
assertBatteryDiffEntry(
resultDiffData.getSystemDiffEntryList().get(0), currentUserId, /*uid=*/ 3L,
ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY, /*consumePercentage=*/ 20.0,
/*foregroundUsageTimeInMs=*/ 50, /*backgroundUsageTimeInMs=*/ 60);
resultDiffData = resultMap.get(0).get(DataProcessor.SELECTED_INDEX_ALL);
assertBatteryDiffEntry(
resultDiffData.getAppDiffEntryList().get(0), currentUserId, /*uid=*/ 2L,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*consumePercentage=*/ 100.0,
/*foregroundUsageTimeInMs=*/ 15, /*backgroundUsageTimeInMs=*/ 25);
resultDiffData = resultMap.get(1).get(DataProcessor.SELECTED_INDEX_ALL);
assertBatteryDiffEntry(
resultDiffData.getAppDiffEntryList().get(0), currentUserId, /*uid=*/ 4L,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*consumePercentage=*/ 50.0,
/*foregroundUsageTimeInMs=*/ 5, /*backgroundUsageTimeInMs=*/ 5);
assertBatteryDiffEntry(
resultDiffData.getAppDiffEntryList().get(1), currentUserId, /*uid=*/ 2L,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*consumePercentage=*/ 25.0,
/*foregroundUsageTimeInMs=*/ 15, /*backgroundUsageTimeInMs=*/ 15);
assertBatteryDiffEntry(
resultDiffData.getSystemDiffEntryList().get(0), currentUserId, /*uid=*/ 3L,
ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY, /*consumePercentage=*/ 25.0,
/*foregroundUsageTimeInMs=*/ 50, /*backgroundUsageTimeInMs=*/ 60);
}
@Test
public void getBatteryUsageMap_multipleUsers_returnsExpectedResult() {
final long[] batteryHistoryKeys = new long[]{
1641052800000L, // 2022-01-02 00:00:00
1641056400000L, // 2022-01-02 01:00:00
1641060000000L // 2022-01-02 02:00:00
};
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap = new HashMap<>();
final int currentUserId = mContext.getUserId();
// Adds the index = 0 data.
Map<String, BatteryHistEntry> entryMap = new HashMap<>();
BatteryHistEntry entry = createBatteryHistEntry(
"package1", "label1", /*consumePower=*/ 5.0, /*uid=*/ 1L, currentUserId,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 10L,
/*backgroundUsageTimeInMs=*/ 20L);
entryMap.put(entry.getKey(), entry);
entry = createBatteryHistEntry(
"package1", "label1", /*consumePower=*/ 10.0, /*uid=*/ 2L, currentUserId + 1,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 10L,
/*backgroundUsageTimeInMs=*/ 20L);
entryMap.put(entry.getKey(), entry);
entry = createBatteryHistEntry(
"package2", "label2", /*consumePower=*/ 5.0, /*uid=*/ 3L, currentUserId + 2,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 20L,
/*backgroundUsageTimeInMs=*/ 30L);
entryMap.put(entry.getKey(), entry);
batteryHistoryMap.put(batteryHistoryKeys[0], entryMap);
// Adds the index = 1 data.
entryMap = new HashMap<>();
entry = createBatteryHistEntry(
"package1", "label1", /*consumePower=*/ 15.0, /*uid=*/ 1L, currentUserId,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 20L,
/*backgroundUsageTimeInMs=*/ 30L);
entryMap.put(entry.getKey(), entry);
entry = createBatteryHistEntry(
"package1", "label1", /*consumePower=*/ 30.0, /*uid=*/ 2L, currentUserId + 1,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 10L,
/*backgroundUsageTimeInMs=*/ 20L);
entryMap.put(entry.getKey(), entry);
entry = createBatteryHistEntry(
"package2", "label2", /*consumePower=*/ 15.0, /*uid=*/ 3L, currentUserId + 2,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 30L,
/*backgroundUsageTimeInMs=*/ 30L);
entryMap.put(entry.getKey(), entry);
batteryHistoryMap.put(batteryHistoryKeys[1], entryMap);
// Adds the index = 2 data.
entryMap = new HashMap<>();
entry = createBatteryHistEntry(
"package1", "label1", /*consumePower=*/ 25.0, /*uid=*/ 1L, currentUserId,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 20L,
/*backgroundUsageTimeInMs=*/ 30L);
entryMap.put(entry.getKey(), entry);
entry = createBatteryHistEntry(
"package1", "label1", /*consumePower=*/ 50.0, /*uid=*/ 2L, currentUserId + 1,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 20L,
/*backgroundUsageTimeInMs=*/ 20L);
entryMap.put(entry.getKey(), entry);
entry = createBatteryHistEntry(
"package2", "label2", /*consumePower=*/ 25.0, /*uid=*/ 3L, currentUserId + 2,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 30L,
/*backgroundUsageTimeInMs=*/ 30L);
entryMap.put(entry.getKey(), entry);
batteryHistoryMap.put(batteryHistoryKeys[2], entryMap);
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay =
new ArrayList<>();
List<Long> timestamps = List.of(batteryHistoryKeys[0], batteryHistoryKeys[2]);
final List<Integer> levels = List.of(100, 100);
hourlyBatteryLevelsPerDay.add(
new BatteryLevelData.PeriodBatteryLevelData(timestamps, levels));
final Map<Integer, Map<Integer, BatteryDiffData>> resultMap =
DataProcessor.getBatteryUsageMap(
mContext, hourlyBatteryLevelsPerDay, batteryHistoryMap);
final BatteryDiffData resultDiffData =
resultMap
.get(DataProcessor.SELECTED_INDEX_ALL)
.get(DataProcessor.SELECTED_INDEX_ALL);
assertBatteryDiffEntry(
resultDiffData.getAppDiffEntryList().get(0), currentUserId, /*uid=*/ 1L,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*consumePercentage=*/ 25.0,
/*foregroundUsageTimeInMs=*/ 10, /*backgroundUsageTimeInMs=*/ 10);
assertBatteryDiffEntry(
resultDiffData.getSystemDiffEntryList().get(0), BatteryUtils.UID_OTHER_USERS,
/*uid=*/ BatteryUtils.UID_OTHER_USERS, ConvertUtils.CONSUMER_TYPE_UID_BATTERY,
/*consumePercentage=*/ 75.0, /*foregroundUsageTimeInMs=*/ 0,
/*backgroundUsageTimeInMs=*/ 0);
assertThat(resultMap.get(0).get(0)).isNotNull();
assertThat(resultMap.get(0).get(DataProcessor.SELECTED_INDEX_ALL)).isNotNull();
}
@Test
public void getBatteryUsageMap_usageTimeExceed_returnsExpectedResult() {
final long[] batteryHistoryKeys = new long[]{
1641052800000L, // 2022-01-02 00:00:00
1641056400000L, // 2022-01-02 01:00:00
1641060000000L // 2022-01-02 02:00:00
};
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap = new HashMap<>();
final int currentUserId = mContext.getUserId();
// Adds the index = 0 data.
Map<String, BatteryHistEntry> entryMap = new HashMap<>();
BatteryHistEntry entry = createBatteryHistEntry(
"package1", "label1", /*consumePower=*/ 0, /*uid=*/ 1L, currentUserId,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 0L,
/*backgroundUsageTimeInMs=*/ 0L);
entryMap.put(entry.getKey(), entry);
batteryHistoryMap.put(batteryHistoryKeys[0], entryMap);
// Adds the index = 1 data.
entryMap = new HashMap<>();
entry = createBatteryHistEntry(
"package1", "label1", /*consumePower=*/ 0, /*uid=*/ 1L, currentUserId,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 0L,
/*backgroundUsageTimeInMs=*/ 0L);
entryMap.put(entry.getKey(), entry);
batteryHistoryMap.put(batteryHistoryKeys[1], entryMap);
// Adds the index = 2 data.
entryMap = new HashMap<>();
entry = createBatteryHistEntry(
"package1", "label1", /*consumePower=*/ 500.0, /*uid=*/ 1L, currentUserId,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 3600000L,
/*backgroundUsageTimeInMs=*/ 7200000L);
entryMap.put(entry.getKey(), entry);
batteryHistoryMap.put(batteryHistoryKeys[2], entryMap);
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay =
new ArrayList<>();
List<Long> timestamps = List.of(batteryHistoryKeys[0], batteryHistoryKeys[2]);
final List<Integer> levels = List.of(100, 100);
hourlyBatteryLevelsPerDay.add(
new BatteryLevelData.PeriodBatteryLevelData(timestamps, levels));
final Map<Integer, Map<Integer, BatteryDiffData>> resultMap =
DataProcessor.getBatteryUsageMap(
mContext, hourlyBatteryLevelsPerDay, batteryHistoryMap);
final BatteryDiffData resultDiffData =
resultMap
.get(DataProcessor.SELECTED_INDEX_ALL)
.get(DataProcessor.SELECTED_INDEX_ALL);
// Verifies the clipped usage time.
final float ratio = (float) (7200) / (float) (3600 + 7200);
final BatteryDiffEntry resultEntry = resultDiffData.getAppDiffEntryList().get(0);
assertThat(resultEntry.mForegroundUsageTimeInMs)
.isEqualTo(Math.round(entry.mForegroundUsageTimeInMs * ratio));
assertThat(resultEntry.mBackgroundUsageTimeInMs)
.isEqualTo(Math.round(entry.mBackgroundUsageTimeInMs * ratio));
assertThat(resultEntry.mConsumePower)
.isEqualTo(entry.mConsumePower * ratio);
assertThat(resultMap.get(0).get(0)).isNotNull();
assertThat(resultMap.get(0).get(DataProcessor.SELECTED_INDEX_ALL)).isNotNull();
}
@Test
public void getBatteryUsageMap_hideApplicationEntries_returnsExpectedResult() {
final long[] batteryHistoryKeys = new long[]{
1641052800000L, // 2022-01-02 00:00:00
1641056400000L, // 2022-01-02 01:00:00
1641060000000L // 2022-01-02 02:00:00
};
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap = new HashMap<>();
final int currentUserId = mContext.getUserId();
// Adds the index = 0 data.
Map<String, BatteryHistEntry> entryMap = new HashMap<>();
BatteryHistEntry entry = createBatteryHistEntry(
"package1", "label1", /*consumePower=*/ 0, /*uid=*/ 1L, currentUserId,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 0L,
/*backgroundUsageTimeInMs=*/ 0L);
entryMap.put(entry.getKey(), entry);
entry = createBatteryHistEntry(
"package2", "label2", /*consumePower=*/ 0, /*uid=*/ 2L, currentUserId,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 0L,
/*backgroundUsageTimeInMs=*/ 0L);
entryMap.put(entry.getKey(), entry);
batteryHistoryMap.put(batteryHistoryKeys[0], entryMap);
// Adds the index = 1 data.
entryMap = new HashMap<>();
entry = createBatteryHistEntry(
"package1", "label1", /*consumePower=*/ 0, /*uid=*/ 1L, currentUserId,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 0L,
/*backgroundUsageTimeInMs=*/ 0L);
entryMap.put(entry.getKey(), entry);
entry = createBatteryHistEntry(
"package2", "label2", /*consumePower=*/ 0, /*uid=*/ 2L, currentUserId,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 0L,
/*backgroundUsageTimeInMs=*/ 0L);
entryMap.put(entry.getKey(), entry);
batteryHistoryMap.put(batteryHistoryKeys[1], entryMap);
// Adds the index = 2 data.
entryMap = new HashMap<>();
entry = createBatteryHistEntry(
"package1", "label1", /*consumePower=*/ 10.0, /*uid=*/ 1L, currentUserId,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 10L,
/*backgroundUsageTimeInMs=*/ 20L);
entryMap.put(entry.getKey(), entry);
entry = createBatteryHistEntry(
"package2", "label2", /*consumePower=*/ 10.0, /*uid=*/ 2L, currentUserId,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 10L,
/*backgroundUsageTimeInMs=*/ 20L);
entryMap.put(entry.getKey(), entry);
batteryHistoryMap.put(batteryHistoryKeys[2], entryMap);
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay =
new ArrayList<>();
List<Long> timestamps = List.of(batteryHistoryKeys[0], batteryHistoryKeys[2]);
final List<Integer> levels = List.of(100, 100);
hourlyBatteryLevelsPerDay.add(
new BatteryLevelData.PeriodBatteryLevelData(timestamps, levels));
when(mPowerUsageFeatureProvider.getHideApplicationEntries(mContext))
.thenReturn(new CharSequence[]{"package1"});
final Map<Integer, Map<Integer, BatteryDiffData>> resultMap =
DataProcessor.getBatteryUsageMap(
mContext, hourlyBatteryLevelsPerDay, batteryHistoryMap);
final BatteryDiffData resultDiffData =
resultMap
.get(DataProcessor.SELECTED_INDEX_ALL)
.get(DataProcessor.SELECTED_INDEX_ALL);
assertBatteryDiffEntry(
resultDiffData.getAppDiffEntryList().get(0), currentUserId, /*uid=*/ 2L,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*consumePercentage=*/ 50.0,
/*foregroundUsageTimeInMs=*/ 10, /*backgroundUsageTimeInMs=*/ 20);
}
@Test
public void getBatteryUsageMap_hideBackgroundUsageTime_returnsExpectedResult() {
final long[] batteryHistoryKeys = new long[]{
1641052800000L, // 2022-01-02 00:00:00
1641056400000L, // 2022-01-02 01:00:00
1641060000000L // 2022-01-02 02:00:00
};
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap = new HashMap<>();
final int currentUserId = mContext.getUserId();
// Adds the index = 0 data.
Map<String, BatteryHistEntry> entryMap = new HashMap<>();
BatteryHistEntry entry = createBatteryHistEntry(
"package1", "label1", /*consumePower=*/ 0, /*uid=*/ 1L, currentUserId,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 0L,
/*backgroundUsageTimeInMs=*/ 0L);
entryMap.put(entry.getKey(), entry);
entry = createBatteryHistEntry(
"package2", "label2", /*consumePower=*/ 0, /*uid=*/ 2L, currentUserId,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 0L,
/*backgroundUsageTimeInMs=*/ 0L);
entryMap.put(entry.getKey(), entry);
batteryHistoryMap.put(batteryHistoryKeys[0], entryMap);
// Adds the index = 1 data.
entryMap = new HashMap<>();
entry = createBatteryHistEntry(
"package1", "label1", /*consumePower=*/ 0, /*uid=*/ 1L, currentUserId,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 0L,
/*backgroundUsageTimeInMs=*/ 0L);
entryMap.put(entry.getKey(), entry);
entry = createBatteryHistEntry(
"package2", "label2", /*consumePower=*/ 0, /*uid=*/ 2L, currentUserId,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 0L,
/*backgroundUsageTimeInMs=*/ 0L);
entryMap.put(entry.getKey(), entry);
batteryHistoryMap.put(batteryHistoryKeys[1], entryMap);
// Adds the index = 2 data.
entryMap = new HashMap<>();
entry = createBatteryHistEntry(
"package1", "label1", /*consumePower=*/ 10.0, /*uid=*/ 1L, currentUserId,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 10L,
/*backgroundUsageTimeInMs=*/ 20L);
entryMap.put(entry.getKey(), entry);
entry = createBatteryHistEntry(
"package2", "label2", /*consumePower=*/ 10.0, /*uid=*/ 2L, currentUserId,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 10L,
/*backgroundUsageTimeInMs=*/ 20L);
entryMap.put(entry.getKey(), entry);
batteryHistoryMap.put(batteryHistoryKeys[2], entryMap);
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay =
new ArrayList<>();
List<Long> timestamps = List.of(batteryHistoryKeys[0], batteryHistoryKeys[2]);
final List<Integer> levels = List.of(100, 100);
hourlyBatteryLevelsPerDay.add(
new BatteryLevelData.PeriodBatteryLevelData(timestamps, levels));
when(mPowerUsageFeatureProvider.getHideBackgroundUsageTimeSet(mContext))
.thenReturn(new HashSet(Arrays.asList((CharSequence) "package2")));
final Map<Integer, Map<Integer, BatteryDiffData>> resultMap =
DataProcessor.getBatteryUsageMap(
mContext, hourlyBatteryLevelsPerDay, batteryHistoryMap);
final BatteryDiffData resultDiffData =
resultMap
.get(DataProcessor.SELECTED_INDEX_ALL)
.get(DataProcessor.SELECTED_INDEX_ALL);
BatteryDiffEntry resultEntry = resultDiffData.getAppDiffEntryList().get(0);
assertThat(resultEntry.mBackgroundUsageTimeInMs).isEqualTo(20);
resultEntry = resultDiffData.getAppDiffEntryList().get(1);
assertThat(resultEntry.mBackgroundUsageTimeInMs).isEqualTo(0);
}
private static Map<Long, Map<String, BatteryHistEntry>> createHistoryMap(
final long[] timestamps, final int[] levels) {
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap = new HashMap<>();
@@ -391,6 +859,23 @@ public class DataProcessorTest {
return batteryHistoryMap;
}
private static BatteryHistEntry createBatteryHistEntry(
final String packageName, final String appLabel, final double consumePower,
final long uid, final long userId, final int consumerType,
final long foregroundUsageTimeInMs, final long backgroundUsageTimeInMs) {
// Only insert required fields.
final ContentValues values = new ContentValues();
values.put(BatteryHistEntry.KEY_PACKAGE_NAME, packageName);
values.put(BatteryHistEntry.KEY_APP_LABEL, appLabel);
values.put(BatteryHistEntry.KEY_UID, uid);
values.put(BatteryHistEntry.KEY_USER_ID, userId);
values.put(BatteryHistEntry.KEY_CONSUMER_TYPE, consumerType);
values.put(BatteryHistEntry.KEY_CONSUME_POWER, consumePower);
values.put(BatteryHistEntry.KEY_FOREGROUND_USAGE_TIME, foregroundUsageTimeInMs);
values.put(BatteryHistEntry.KEY_BACKGROUND_USAGE_TIME, backgroundUsageTimeInMs);
return new BatteryHistEntry(values);
}
private static void verifyExpectedBatteryLevelData(
final BatteryLevelData resultData,
final List<Long> expectedDailyTimestamps,
@@ -451,4 +936,15 @@ public class DataProcessorTest {
.isEqualTo(expectedEnd.getTimeInMillis());
}
private static void assertBatteryDiffEntry(
final BatteryDiffEntry entry, final long userId, final long uid,
final int consumerType, final double consumePercentage,
final long foregroundUsageTimeInMs, final long backgroundUsageTimeInMs) {
assertThat(entry.mBatteryHistEntry.mUserId).isEqualTo(userId);
assertThat(entry.mBatteryHistEntry.mUid).isEqualTo(uid);
assertThat(entry.mBatteryHistEntry.mConsumerType).isEqualTo(consumerType);
assertThat(entry.getPercentOfTotal()).isEqualTo(consumePercentage);
assertThat(entry.mForegroundUsageTimeInMs).isEqualTo(foregroundUsageTimeInMs);
assertThat(entry.mBackgroundUsageTimeInMs).isEqualTo(backgroundUsageTimeInMs);
}
}