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:
@@ -266,8 +266,9 @@ public class BatteryChartPreferenceControllerV2 extends AbstractPreferenceContro
|
|||||||
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap) {
|
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap) {
|
||||||
Log.d(TAG, "setBatteryHistoryMap() " + (batteryHistoryMap == null ? "null"
|
Log.d(TAG, "setBatteryHistoryMap() " + (batteryHistoryMap == null ? "null"
|
||||||
: ("size=" + batteryHistoryMap.size())));
|
: ("size=" + batteryHistoryMap.size())));
|
||||||
|
// TODO: implement the callback function.
|
||||||
final BatteryLevelData batteryLevelData =
|
final BatteryLevelData batteryLevelData =
|
||||||
DataProcessor.getBatteryLevelData(mContext, batteryHistoryMap);
|
DataProcessor.getBatteryLevelData(mContext, mHandler, batteryHistoryMap, null);
|
||||||
Log.d(TAG, "getBatteryLevelData: " + batteryLevelData);
|
Log.d(TAG, "getBatteryLevelData: " + batteryLevelData);
|
||||||
if (batteryLevelData == null) {
|
if (batteryLevelData == null) {
|
||||||
mDailyViewModel = null;
|
mDailyViewModel = null;
|
||||||
|
@@ -16,7 +16,9 @@
|
|||||||
|
|
||||||
package com.android.settings.fuelgauge.batteryusage;
|
package com.android.settings.fuelgauge.batteryusage;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/** Wraps the battery usage diff data for each entry used for battery usage app 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> mAppEntries;
|
||||||
private final List<BatteryDiffEntry> mSystemEntries;
|
private final List<BatteryDiffEntry> mSystemEntries;
|
||||||
|
|
||||||
|
/** Constructor for the diff entries which already have totalConsumePower value. */
|
||||||
public BatteryDiffData(
|
public BatteryDiffData(
|
||||||
List<BatteryDiffEntry> appDiffEntries, List<BatteryDiffEntry> systemDiffEntries) {
|
@NonNull List<BatteryDiffEntry> appDiffEntries,
|
||||||
mAppEntries = appDiffEntries == null ? new ArrayList<>() : appDiffEntries;
|
@NonNull List<BatteryDiffEntry> systemDiffEntries) {
|
||||||
mSystemEntries = systemDiffEntries == null ? new ArrayList<>() : 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() {
|
public List<BatteryDiffEntry> getAppDiffEntryList() {
|
||||||
@@ -38,9 +54,15 @@ public class BatteryDiffData {
|
|||||||
return mSystemEntries;
|
return mSystemEntries;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Sets total consume power for each entry. */
|
// Sets total consume power for each entry.
|
||||||
public void setTotalConsumePowerForAllEntries(double totalConsumePower) {
|
private void setTotalConsumePowerForAllEntries(final double totalConsumePower) {
|
||||||
mAppEntries.forEach(diffEntry -> diffEntry.setTotalConsumePower(totalConsumePower));
|
mAppEntries.forEach(diffEntry -> diffEntry.setTotalConsumePower(totalConsumePower));
|
||||||
mSystemEntries.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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -18,21 +18,36 @@ package com.android.settings.fuelgauge.batteryusage;
|
|||||||
|
|
||||||
import static com.android.settings.fuelgauge.batteryusage.ConvertUtils.utcToLocalTime;
|
import static com.android.settings.fuelgauge.batteryusage.ConvertUtils.utcToLocalTime;
|
||||||
|
|
||||||
|
import android.content.ContentValues;
|
||||||
import android.content.Context;
|
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.text.format.DateUtils;
|
||||||
|
import android.util.ArraySet;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import com.android.internal.annotations.VisibleForTesting;
|
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 com.android.settingslib.fuelgauge.BatteryStatus;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
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
|
* 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 String TAG = "DataProcessor";
|
||||||
private static final int MIN_DAILY_DATA_SIZE = 2;
|
private static final int MIN_DAILY_DATA_SIZE = 2;
|
||||||
private static final int MIN_TIMESTAMP_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. */
|
/** A fake package name to represent no BatteryEntry data. */
|
||||||
public static final String FAKE_PACKAGE_NAME = "fake_package";
|
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() {
|
private DataProcessor() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,11 +90,14 @@ public final class DataProcessor {
|
|||||||
@Nullable
|
@Nullable
|
||||||
public static BatteryLevelData getBatteryLevelData(
|
public static BatteryLevelData getBatteryLevelData(
|
||||||
Context context,
|
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()) {
|
if (batteryHistoryMap == null || batteryHistoryMap.isEmpty()) {
|
||||||
Log.d(TAG, "getBatteryLevelData() returns null");
|
Log.d(TAG, "getBatteryLevelData() returns null");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
handler = handler != null ? handler : new Handler(Looper.getMainLooper());
|
||||||
// Process raw history map data into hourly timestamps.
|
// Process raw history map data into hourly timestamps.
|
||||||
final Map<Long, Map<String, BatteryHistEntry>> processedBatteryHistoryMap =
|
final Map<Long, Map<String, BatteryHistEntry>> processedBatteryHistoryMap =
|
||||||
getHistoryMapWithExpectedTimestamps(context, batteryHistoryMap);
|
getHistoryMapWithExpectedTimestamps(context, batteryHistoryMap);
|
||||||
@@ -70,11 +105,33 @@ public final class DataProcessor {
|
|||||||
final BatteryLevelData batteryLevelData =
|
final BatteryLevelData batteryLevelData =
|
||||||
getLevelDataThroughProcessedHistoryMap(context, processedBatteryHistoryMap);
|
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 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.
|
* @return Returns the processed history map which has interpolated to every hour data.
|
||||||
* The start and end timestamp must be the even hours.
|
* The start and end timestamp must be the even hours.
|
||||||
@@ -187,7 +244,7 @@ public final class DataProcessor {
|
|||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
static boolean isFromFullCharge(@Nullable final Map<String, BatteryHistEntry> entryList) {
|
static boolean isFromFullCharge(@Nullable final Map<String, BatteryHistEntry> entryList) {
|
||||||
if (entryList == null) {
|
if (entryList == null) {
|
||||||
Log.d(TAG, "entryList is nul in isFromFullCharge()");
|
Log.d(TAG, "entryList is null in isFromFullCharge()");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
final List<String> entryKeys = new ArrayList<>(entryList.keySet());
|
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) {
|
static long[] findNearestTimestamp(final List<Long> timestamps, final long target) {
|
||||||
final long[] results = new long[] {Long.MIN_VALUE, Long.MAX_VALUE};
|
final long[] results = new long[] {Long.MIN_VALUE, Long.MAX_VALUE};
|
||||||
// Searches the nearest lower and upper timestamp value.
|
// Searches the nearest lower and upper timestamp value.
|
||||||
for (long timestamp : timestamps) {
|
timestamps.forEach(timestamp -> {
|
||||||
if (timestamp <= target && timestamp > results[0]) {
|
if (timestamp <= target && timestamp > results[0]) {
|
||||||
results[0] = timestamp;
|
results[0] = timestamp;
|
||||||
}
|
}
|
||||||
if (timestamp >= target && timestamp < results[1]) {
|
if (timestamp >= target && timestamp < results[1]) {
|
||||||
results[1] = timestamp;
|
results[1] = timestamp;
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
// Uses zero value to represent invalid searching result.
|
// Uses zero value to represent invalid searching result.
|
||||||
results[0] = results[0] == Long.MIN_VALUE ? 0 : results[0];
|
results[0] = results[0] == Long.MIN_VALUE ? 0 : results[0];
|
||||||
results[1] = results[1] == Long.MAX_VALUE ? 0 : results[1];
|
results[1] = results[1] == Long.MAX_VALUE ? 0 : results[1];
|
||||||
@@ -234,6 +291,40 @@ public final class DataProcessor {
|
|||||||
return nextDayCalendar.getTimeInMillis();
|
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
|
* Interpolates history map based on expected timestamp slots and processes the corner case when
|
||||||
* the expected start timestamp is earlier than what we have.
|
* the expected start timestamp is earlier than what we have.
|
||||||
@@ -458,11 +549,454 @@ public final class DataProcessor {
|
|||||||
return Math.round(batteryLevelCounter / entryMap.size());
|
return Math.round(batteryLevelCounter / entryMap.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void log(Context context, String content, long timestamp,
|
private static void insertHourlyUsageDiffData(
|
||||||
BatteryHistEntry entry) {
|
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) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, String.format(entry != null ? "%s %s:\n%s" : "%s %s:%s",
|
Log.d(TAG, String.format(entry != null ? "%s %s:\n%s" : "%s %s:%s",
|
||||||
utcToLocalTime(context, timestamp), content, entry));
|
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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -19,11 +19,16 @@ package com.android.settings.fuelgauge.batteryusage;
|
|||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
import static org.mockito.Mockito.spy;
|
import static org.mockito.Mockito.spy;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.text.format.DateUtils;
|
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.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
@@ -35,6 +40,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
@@ -45,18 +51,30 @@ public class DataProcessorTest {
|
|||||||
|
|
||||||
private Context mContext;
|
private Context mContext;
|
||||||
|
|
||||||
|
private FakeFeatureFactory mFeatureFactory;
|
||||||
|
private PowerUsageFeatureProvider mPowerUsageFeatureProvider;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
MockitoAnnotations.initMocks(this);
|
MockitoAnnotations.initMocks(this);
|
||||||
TimeZone.setDefault(TimeZone.getTimeZone("GMT+8"));
|
TimeZone.setDefault(TimeZone.getTimeZone("GMT+8"));
|
||||||
|
|
||||||
mContext = spy(RuntimeEnvironment.application);
|
mContext = spy(RuntimeEnvironment.application);
|
||||||
|
mFeatureFactory = FakeFeatureFactory.setupForTest();
|
||||||
|
mPowerUsageFeatureProvider = mFeatureFactory.powerUsageFeatureProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getBatteryLevelData_emptyHistoryMap_returnNull() {
|
public void getBatteryLevelData_emptyHistoryMap_returnNull() {
|
||||||
assertThat(DataProcessor.getBatteryLevelData(mContext, null)).isNull();
|
assertThat(DataProcessor.getBatteryLevelData(
|
||||||
assertThat(DataProcessor.getBatteryLevelData(mContext, new HashMap<>())).isNull();
|
mContext,
|
||||||
|
/*handler=*/ null,
|
||||||
|
/*batteryHistoryMap=*/ null,
|
||||||
|
/*asyncResponseDelegate=*/ null))
|
||||||
|
.isNull();
|
||||||
|
assertThat(DataProcessor.getBatteryLevelData(
|
||||||
|
mContext, /*handler=*/ null, new HashMap<>(), /*asyncResponseDelegate=*/ null))
|
||||||
|
.isNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -67,7 +85,9 @@ public class DataProcessorTest {
|
|||||||
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap =
|
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap =
|
||||||
createHistoryMap(timestamps, levels);
|
createHistoryMap(timestamps, levels);
|
||||||
|
|
||||||
assertThat(DataProcessor.getBatteryLevelData(mContext, batteryHistoryMap)).isNull();
|
assertThat(DataProcessor.getBatteryLevelData(
|
||||||
|
mContext, /*handler=*/ null, batteryHistoryMap, /*asyncResponseDelegate=*/ null))
|
||||||
|
.isNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -79,7 +99,11 @@ public class DataProcessorTest {
|
|||||||
createHistoryMap(timestamps, levels);
|
createHistoryMap(timestamps, levels);
|
||||||
|
|
||||||
final BatteryLevelData resultData =
|
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<Long> expectedDailyTimestamps = List.of(timestamps[0], timestamps[2]);
|
||||||
final List<Integer> expectedDailyLevels = List.of(levels[0], levels[2]);
|
final List<Integer> expectedDailyLevels = List.of(levels[0], levels[2]);
|
||||||
@@ -377,6 +401,450 @@ public class DataProcessorTest {
|
|||||||
.isEqualTo(1640966400000L);
|
.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(
|
private static Map<Long, Map<String, BatteryHistEntry>> createHistoryMap(
|
||||||
final long[] timestamps, final int[] levels) {
|
final long[] timestamps, final int[] levels) {
|
||||||
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap = new HashMap<>();
|
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap = new HashMap<>();
|
||||||
@@ -391,6 +859,23 @@ public class DataProcessorTest {
|
|||||||
return batteryHistoryMap;
|
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(
|
private static void verifyExpectedBatteryLevelData(
|
||||||
final BatteryLevelData resultData,
|
final BatteryLevelData resultData,
|
||||||
final List<Long> expectedDailyTimestamps,
|
final List<Long> expectedDailyTimestamps,
|
||||||
@@ -451,4 +936,15 @@ public class DataProcessorTest {
|
|||||||
.isEqualTo(expectedEnd.getTimeInMillis());
|
.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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user