Merge "Refine the interpolation rule and clip the usage time data" into sc-dev

This commit is contained in:
TreeHugger Robot
2021-05-07 09:35:35 +00:00
committed by Android (Google) Code Review
3 changed files with 114 additions and 24 deletions

View File

@@ -223,7 +223,7 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
ConvertUtils.getIndexedUsageMap( ConvertUtils.getIndexedUsageMap(
mPrefContext, /*timeSlotSize=*/ CHART_LEVEL_ARRAY_SIZE - 1, mPrefContext, /*timeSlotSize=*/ CHART_LEVEL_ARRAY_SIZE - 1,
mBatteryHistoryKeys, batteryHistoryMap, mBatteryHistoryKeys, batteryHistoryMap,
/*purgeLowPercentageData=*/ true); /*purgeLowPercentageAndFakeData=*/ true);
forceRefreshUi(); forceRefreshUi();
Log.d(TAG, String.format( Log.d(TAG, String.format(

View File

@@ -18,12 +18,15 @@ import android.content.ContentValues;
import android.content.Context; import android.content.Context;
import android.os.BatteryUsageStats; import android.os.BatteryUsageStats;
import android.os.UserHandle; import android.os.UserHandle;
import android.text.format.DateUtils;
import android.util.Log;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.time.Duration;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
@@ -41,6 +44,8 @@ public final class ConvertUtils {
private static final Map<String, BatteryHistEntry> EMPTY_BATTERY_MAP = new HashMap<>(); private static final Map<String, BatteryHistEntry> EMPTY_BATTERY_MAP = new HashMap<>();
private static final BatteryHistEntry EMPTY_BATTERY_HIST_ENTRY = private static final BatteryHistEntry EMPTY_BATTERY_HIST_ENTRY =
new BatteryHistEntry(new ContentValues()); new BatteryHistEntry(new ContentValues());
// Maximum total time value for each slot cumulative data at most 2 hours.
private static final float TOTAL_TIME_THRESHOLD = DateUtils.HOUR_IN_MILLIS * 2;
@VisibleForTesting @VisibleForTesting
static double PERCENTAGE_OF_TOTAL_THRESHOLD = 1f; static double PERCENTAGE_OF_TOTAL_THRESHOLD = 1f;
@@ -145,7 +150,10 @@ public final class ConvertUtils {
final int timeSlotSize, final int timeSlotSize,
final long[] batteryHistoryKeys, final long[] batteryHistoryKeys,
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap, final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap,
final boolean purgeLowPercentageData) { final boolean purgeLowPercentageAndFakeData) {
if (batteryHistoryMap == null || batteryHistoryMap.isEmpty()) {
return new HashMap<>();
}
final Map<Integer, List<BatteryDiffEntry>> resultMap = new HashMap<>(); final Map<Integer, List<BatteryDiffEntry>> resultMap = new HashMap<>();
// Each time slot usage diff data = // Each time slot usage diff data =
// Math.abs(timestamp[i+2] data - timestamp[i+1] data) + // Math.abs(timestamp[i+2] data - timestamp[i+1] data) +
@@ -155,16 +163,10 @@ public final class ConvertUtils {
for (int index = 0; index < timeSlotSize; index++) { for (int index = 0; index < timeSlotSize; index++) {
final Long currentTimestamp = final Long currentTimestamp =
Long.valueOf(batteryHistoryKeys[index * timestampStride]); Long.valueOf(batteryHistoryKeys[index * timestampStride]);
// Uses empty list if the timestamp is default value.
if (currentTimestamp == 0) {
resultMap.put(Integer.valueOf(index), new ArrayList<BatteryDiffEntry>());
continue;
}
final Long nextTimestamp = final Long nextTimestamp =
Long.valueOf(batteryHistoryKeys[index * timestampStride + 1]); Long.valueOf(batteryHistoryKeys[index * timestampStride + 1]);
final Long nextTwoTimestamp = final Long nextTwoTimestamp =
Long.valueOf(batteryHistoryKeys[index * timestampStride + 2]); Long.valueOf(batteryHistoryKeys[index * timestampStride + 2]);
// Fetches BatteryHistEntry data from corresponding time slot. // Fetches BatteryHistEntry data from corresponding time slot.
final Map<String, BatteryHistEntry> currentBatteryHistMap = final Map<String, BatteryHistEntry> currentBatteryHistMap =
batteryHistoryMap.getOrDefault(currentTimestamp, EMPTY_BATTERY_MAP); batteryHistoryMap.getOrDefault(currentTimestamp, EMPTY_BATTERY_MAP);
@@ -172,8 +174,17 @@ public final class ConvertUtils {
batteryHistoryMap.getOrDefault(nextTimestamp, EMPTY_BATTERY_MAP); batteryHistoryMap.getOrDefault(nextTimestamp, EMPTY_BATTERY_MAP);
final Map<String, BatteryHistEntry> nextTwoBatteryHistMap = final Map<String, BatteryHistEntry> nextTwoBatteryHistMap =
batteryHistoryMap.getOrDefault(nextTwoTimestamp, EMPTY_BATTERY_MAP); 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()) {
resultMap.put(Integer.valueOf(index), new ArrayList<BatteryDiffEntry>());
continue;
}
// Collects all keys in these three time slot records as population. // Collects all keys in these three time slot records as all populations.
final Set<String> allBatteryHistEntryKeys = new HashSet<>(); final Set<String> allBatteryHistEntryKeys = new HashSet<>();
allBatteryHistEntryKeys.addAll(currentBatteryHistMap.keySet()); allBatteryHistEntryKeys.addAll(currentBatteryHistMap.keySet());
allBatteryHistEntryKeys.addAll(nextBatteryHistMap.keySet()); allBatteryHistEntryKeys.addAll(nextBatteryHistMap.keySet());
@@ -193,12 +204,12 @@ public final class ConvertUtils {
final BatteryHistEntry nextTwoEntry = final BatteryHistEntry nextTwoEntry =
nextTwoBatteryHistMap.getOrDefault(key, EMPTY_BATTERY_HIST_ENTRY); nextTwoBatteryHistMap.getOrDefault(key, EMPTY_BATTERY_HIST_ENTRY);
// Cumulative values is a specific time slot for a specific app. // Cumulative values is a specific time slot for a specific app.
final long foregroundUsageTimeInMs = long foregroundUsageTimeInMs =
getDiffValue( getDiffValue(
currentEntry.mForegroundUsageTimeInMs, currentEntry.mForegroundUsageTimeInMs,
nextEntry.mForegroundUsageTimeInMs, nextEntry.mForegroundUsageTimeInMs,
nextTwoEntry.mForegroundUsageTimeInMs); nextTwoEntry.mForegroundUsageTimeInMs);
final long backgroundUsageTimeInMs = long backgroundUsageTimeInMs =
getDiffValue( getDiffValue(
currentEntry.mBackgroundUsageTimeInMs, currentEntry.mBackgroundUsageTimeInMs,
nextEntry.mBackgroundUsageTimeInMs, nextEntry.mBackgroundUsageTimeInMs,
@@ -221,6 +232,21 @@ public final class ConvertUtils {
if (selectedBatteryEntry == null) { if (selectedBatteryEntry == null) {
continue; continue;
} }
// Force 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_TIME_THRESHOLD) {
final float ratio = TOTAL_TIME_THRESHOLD / totalUsageTimeInMs;
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);
}
batteryDiffEntryList.add( batteryDiffEntryList.add(
new BatteryDiffEntry( new BatteryDiffEntry(
context, context,
@@ -234,10 +260,10 @@ public final class ConvertUtils {
diffEntry.setTotalConsumePower(totalConsumePower); diffEntry.setTotalConsumePower(totalConsumePower);
} }
} }
insert24HoursData(BatteryChartView.SELECTED_INDEX_ALL, resultMap); if (purgeLowPercentageAndFakeData) {
if (purgeLowPercentageData) { purgeLowPercentageAndFakeData(resultMap);
purgeLowPercentageData(resultMap);
} }
insert24HoursData(BatteryChartView.SELECTED_INDEX_ALL, resultMap);
return resultMap; return resultMap;
} }
@@ -273,13 +299,15 @@ public final class ConvertUtils {
indexedUsageMap.put(Integer.valueOf(desiredIndex), resultList); indexedUsageMap.put(Integer.valueOf(desiredIndex), resultList);
} }
private static void purgeLowPercentageData( // Removes low percentage data and fake usage data, which will be zero value.
private static void purgeLowPercentageAndFakeData(
final Map<Integer, List<BatteryDiffEntry>> indexedUsageMap) { final Map<Integer, List<BatteryDiffEntry>> indexedUsageMap) {
for (List<BatteryDiffEntry> entries : indexedUsageMap.values()) { for (List<BatteryDiffEntry> entries : indexedUsageMap.values()) {
final Iterator<BatteryDiffEntry> iterator = entries.iterator(); final Iterator<BatteryDiffEntry> iterator = entries.iterator();
while (iterator.hasNext()) { while (iterator.hasNext()) {
final BatteryDiffEntry entry = iterator.next(); final BatteryDiffEntry entry = iterator.next();
if (entry.getPercentOfTotal() < PERCENTAGE_OF_TOTAL_THRESHOLD) { if (entry.getPercentOfTotal() < PERCENTAGE_OF_TOTAL_THRESHOLD
|| FAKE_PACKAGE_NAME.equals(entry.getPackageName())) {
iterator.remove(); iterator.remove();
} }
} }

View File

@@ -140,6 +140,21 @@ public final class ConvertUtilsTest {
.isEqualTo(ConvertUtils.FAKE_PACKAGE_NAME); .isEqualTo(ConvertUtils.FAKE_PACKAGE_NAME);
} }
@Test
public void testGetIndexedUsageMap_nullOrEmptyHistoryMap_returnEmptyCollection() {
final int timeSlotSize = 2;
final long[] batteryHistoryKeys = new long[] {101L, 102L, 103L, 104L, 105L};
assertThat(ConvertUtils.getIndexedUsageMap(
mContext, timeSlotSize, batteryHistoryKeys,
/*batteryHistoryMap=*/ null, /*purgeLowPercentageAndFakeData=*/ true))
.isEmpty();
assertThat(ConvertUtils.getIndexedUsageMap(
mContext, timeSlotSize, batteryHistoryKeys,
new HashMap<Long, Map<String, BatteryHistEntry>>(),
/*purgeLowPercentageAndFakeData=*/ true))
.isEmpty();
}
@Test @Test
public void testGetIndexedUsageMap_returnsExpectedResult() { public void testGetIndexedUsageMap_returnsExpectedResult() {
// Creates the fake testing data. // Creates the fake testing data.
@@ -147,21 +162,25 @@ public final class ConvertUtilsTest {
final long[] batteryHistoryKeys = new long[] {101L, 102L, 103L, 104L, 105L}; final long[] batteryHistoryKeys = new long[] {101L, 102L, 103L, 104L, 105L};
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap = final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap =
new HashMap<>(); new HashMap<>();
final BatteryHistEntry fakeEntry = createBatteryHistEntry(
ConvertUtils.FAKE_PACKAGE_NAME, "fake_label", 0, 0L, 0L, 0L);
// Adds the index = 0 data. // Adds the index = 0 data.
Map<String, BatteryHistEntry> entryMap = new HashMap<>(); Map<String, BatteryHistEntry> entryMap = new HashMap<>();
BatteryHistEntry entry = createBatteryHistEntry( BatteryHistEntry entry = createBatteryHistEntry(
"package1", "label1", 5.0, 1L, 10L, 20L); "package1", "label1", 5.0, 1L, 10L, 20L);
entryMap.put(entry.getKey(), entry); entryMap.put(entry.getKey(), entry);
entryMap.put(fakeEntry.getKey(), fakeEntry);
batteryHistoryMap.put(Long.valueOf(batteryHistoryKeys[0]), entryMap); batteryHistoryMap.put(Long.valueOf(batteryHistoryKeys[0]), entryMap);
// Adds the index = 1 data. // Adds the index = 1 data.
batteryHistoryMap.put( entryMap = new HashMap<>();
Long.valueOf(batteryHistoryKeys[1]), entryMap.put(fakeEntry.getKey(), fakeEntry);
new HashMap<String, BatteryHistEntry>()); batteryHistoryMap.put(Long.valueOf(batteryHistoryKeys[1]), entryMap);
// Adds the index = 2 data. // Adds the index = 2 data.
entryMap = new HashMap<>(); entryMap = new HashMap<>();
entry = createBatteryHistEntry( entry = createBatteryHistEntry(
"package2", "label2", 10.0, 2L, 15L, 25L); "package2", "label2", 10.0, 2L, 15L, 25L);
entryMap.put(entry.getKey(), entry); entryMap.put(entry.getKey(), entry);
entryMap.put(fakeEntry.getKey(), fakeEntry);
batteryHistoryMap.put(Long.valueOf(batteryHistoryKeys[2]), entryMap); batteryHistoryMap.put(Long.valueOf(batteryHistoryKeys[2]), entryMap);
// Adds the index = 3 data. // Adds the index = 3 data.
entryMap = new HashMap<>(); entryMap = new HashMap<>();
@@ -171,6 +190,7 @@ public final class ConvertUtilsTest {
entry = createBatteryHistEntry( entry = createBatteryHistEntry(
"package3", "label3", 5.0, 3L, 5L, 5L); "package3", "label3", 5.0, 3L, 5L, 5L);
entryMap.put(entry.getKey(), entry); entryMap.put(entry.getKey(), entry);
entryMap.put(fakeEntry.getKey(), fakeEntry);
batteryHistoryMap.put(Long.valueOf(batteryHistoryKeys[3]), entryMap); batteryHistoryMap.put(Long.valueOf(batteryHistoryKeys[3]), entryMap);
// Adds the index = 4 data. // Adds the index = 4 data.
entryMap = new HashMap<>(); entryMap = new HashMap<>();
@@ -183,12 +203,13 @@ public final class ConvertUtilsTest {
entry = createBatteryHistEntry( entry = createBatteryHistEntry(
"package3", "label3", 5.0, 3L, 5L, 5L); "package3", "label3", 5.0, 3L, 5L, 5L);
entryMap.put(entry.getKey(), entry); entryMap.put(entry.getKey(), entry);
entryMap.put(fakeEntry.getKey(), fakeEntry);
batteryHistoryMap.put(Long.valueOf(batteryHistoryKeys[4]), entryMap); batteryHistoryMap.put(Long.valueOf(batteryHistoryKeys[4]), entryMap);
final Map<Integer, List<BatteryDiffEntry>> resultMap = final Map<Integer, List<BatteryDiffEntry>> resultMap =
ConvertUtils.getIndexedUsageMap( ConvertUtils.getIndexedUsageMap(
mContext, timeSlotSize, batteryHistoryKeys, batteryHistoryMap, mContext, timeSlotSize, batteryHistoryKeys, batteryHistoryMap,
/*purgeLowPercentageData=*/ false); /*purgeLowPercentageAndFakeData=*/ false);
assertThat(resultMap).hasSize(3); assertThat(resultMap).hasSize(3);
// Verifies the first timestamp result. // Verifies the first timestamp result.
@@ -213,7 +234,7 @@ public final class ConvertUtilsTest {
final Map<Integer, List<BatteryDiffEntry>> purgedResultMap = final Map<Integer, List<BatteryDiffEntry>> purgedResultMap =
ConvertUtils.getIndexedUsageMap( ConvertUtils.getIndexedUsageMap(
mContext, timeSlotSize, batteryHistoryKeys, batteryHistoryMap, mContext, timeSlotSize, batteryHistoryKeys, batteryHistoryMap,
/*purgeLowPercentageData=*/ true); /*purgeLowPercentageAndFakeData=*/ true);
assertThat(purgedResultMap).hasSize(3); assertThat(purgedResultMap).hasSize(3);
// Verifies the first timestamp result. // Verifies the first timestamp result.
@@ -225,8 +246,49 @@ public final class ConvertUtilsTest {
assertBatteryDiffEntry(entryList.get(0), 75, 40L, 50L); assertBatteryDiffEntry(entryList.get(0), 75, 40L, 50L);
// Verifies the last 24 hours aggregate result. // Verifies the last 24 hours aggregate result.
entryList = purgedResultMap.get(Integer.valueOf(-1)); entryList = purgedResultMap.get(Integer.valueOf(-1));
assertThat(entryList).hasSize(2);
// Verifies the fake data is cleared out.
assertThat(entryList.get(0).getPackageName())
.isNotEqualTo(ConvertUtils.FAKE_PACKAGE_NAME);
assertThat(entryList.get(1).getPackageName())
.isNotEqualTo(ConvertUtils.FAKE_PACKAGE_NAME);
}
@Test
public void testGetIndexedUsageMap_usageTimeExceed_returnsExpectedResult() {
final int timeSlotSize = 1;
final long[] batteryHistoryKeys = new long[] {101L, 102L, 103L};
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap =
new HashMap<>();
final BatteryHistEntry fakeEntry = createBatteryHistEntry(
ConvertUtils.FAKE_PACKAGE_NAME, "fake_label", 0, 0L, 0L, 0L);
// Adds the index = 0 data.
Map<String, BatteryHistEntry> entryMap = new HashMap<>();
entryMap.put(fakeEntry.getKey(), fakeEntry);
batteryHistoryMap.put(Long.valueOf(batteryHistoryKeys[0]), entryMap);
// Adds the index = 1 data.
entryMap = new HashMap<>();
entryMap.put(fakeEntry.getKey(), fakeEntry);
batteryHistoryMap.put(Long.valueOf(batteryHistoryKeys[1]), entryMap);
// Adds the index = 2 data.
entryMap = new HashMap<>();
final BatteryHistEntry entry = createBatteryHistEntry(
"package3", "label3", 500, 5L, 3600000L, 7200000L);
entryMap.put(entry.getKey(), entry);
batteryHistoryMap.put(Long.valueOf(batteryHistoryKeys[2]), entryMap);
final Map<Integer, List<BatteryDiffEntry>> purgedResultMap =
ConvertUtils.getIndexedUsageMap(
mContext, timeSlotSize, batteryHistoryKeys, batteryHistoryMap,
/*purgeLowPercentageAndFakeData=*/ true);
assertThat(purgedResultMap).hasSize(2);
final List<BatteryDiffEntry> entryList = purgedResultMap.get(0);
assertThat(entryList).hasSize(1); assertThat(entryList).hasSize(1);
assertBatteryDiffEntry(entryList.get(0), 68, 40L, 50L); // Verifies the clipped usage time.
final BatteryDiffEntry resultEntry = entryList.get(0);
assertThat(resultEntry.mForegroundUsageTimeInMs).isEqualTo(2400000);
assertThat(resultEntry.mBackgroundUsageTimeInMs).isEqualTo(4800000);
} }
@Test @Test