Add data validator to verify and log abnormal queried history data

Bug: 177406865
Test: make SettingsRoboTests
Test: make SettingsGoogleRoboTests
Change-Id: I331922cf0b0e946066a72eb0bfcf9885dde9a7ce
This commit is contained in:
ykhung
2021-04-21 01:09:15 +08:00
committed by YUKAI HUNG
parent e8063609eb
commit 70bef1049f
6 changed files with 128 additions and 7 deletions

View File

@@ -55,6 +55,8 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
private static final String TAG = "BatteryChartPreferenceController"; private static final String TAG = "BatteryChartPreferenceController";
private static final int CHART_KEY_ARRAY_SIZE = 25; private static final int CHART_KEY_ARRAY_SIZE = 25;
private static final int CHART_LEVEL_ARRAY_SIZE = 13; private static final int CHART_LEVEL_ARRAY_SIZE = 13;
private static final long VALID_USAGE_TIME_DURATION = DateUtils.HOUR_IN_MILLIS * 2;
private static final long VALID_DIFF_DURATION = DateUtils.MINUTE_IN_MILLIS * 3;
@VisibleForTesting @VisibleForTesting
Map<Integer, List<BatteryDiffEntry>> mBatteryIndexedMap; Map<Integer, List<BatteryDiffEntry>> mBatteryIndexedMap;
@@ -179,6 +181,7 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
final List<Long> batteryHistoryKeyList = final List<Long> batteryHistoryKeyList =
new ArrayList<Long>(batteryHistoryMap.keySet()); new ArrayList<Long>(batteryHistoryMap.keySet());
Collections.sort(batteryHistoryKeyList); Collections.sort(batteryHistoryKeyList);
validateSlotTimestamp(batteryHistoryKeyList);
mBatteryHistoryKeys = new long[CHART_KEY_ARRAY_SIZE]; mBatteryHistoryKeys = new long[CHART_KEY_ARRAY_SIZE];
final int elementSize = Math.min(batteryHistoryKeyList.size(), CHART_KEY_ARRAY_SIZE); final int elementSize = Math.min(batteryHistoryKeyList.size(), CHART_KEY_ARRAY_SIZE);
final int offset = CHART_KEY_ARRAY_SIZE - elementSize; final int offset = CHART_KEY_ARRAY_SIZE - elementSize;
@@ -270,6 +273,10 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
} else { } else {
appEntries.add(entry); appEntries.add(entry);
} }
// Validates the usage time if users click a specific slot.
if (mTrapezoidIndex >= 0) {
validateUsageTime(entry);
}
}); });
Collections.sort(appEntries, BatteryDiffEntry.COMPARATOR); Collections.sort(appEntries, BatteryDiffEntry.COMPARATOR);
Collections.sort(systemEntries, BatteryDiffEntry.COMPARATOR); Collections.sort(systemEntries, BatteryDiffEntry.COMPARATOR);
@@ -289,7 +296,7 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
final String appLabel = entry.getAppLabel(); final String appLabel = entry.getAppLabel();
final Drawable appIcon = entry.getAppIcon(); final Drawable appIcon = entry.getAppIcon();
if (TextUtils.isEmpty(appLabel) || appIcon == null) { if (TextUtils.isEmpty(appLabel) || appIcon == null) {
Log.w(TAG, "cannot find app resource:" + entry.mBatteryHistEntry); Log.w(TAG, "cannot find app resource for\n" + entry);
continue; continue;
} }
final String prefKey = entry.mBatteryHistEntry.getKey(); final String prefKey = entry.mBatteryHistEntry.getKey();
@@ -389,4 +396,39 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
} }
return builder.toString(); return builder.toString();
} }
@VisibleForTesting
static boolean validateUsageTime(BatteryDiffEntry entry) {
final long foregroundUsageTimeInMs = entry.mForegroundUsageTimeInMs;
final long backgroundUsageTimeInMs = entry.mBackgroundUsageTimeInMs;
final long totalUsageTimeInMs = foregroundUsageTimeInMs + backgroundUsageTimeInMs;
if (foregroundUsageTimeInMs > VALID_USAGE_TIME_DURATION
|| backgroundUsageTimeInMs > VALID_USAGE_TIME_DURATION
|| totalUsageTimeInMs > VALID_USAGE_TIME_DURATION) {
Log.e(TAG, "validateUsageTime() fail for\n" + entry);
return false;
}
return true;
}
@VisibleForTesting
static boolean validateSlotTimestamp(List<Long> batteryHistoryKeys) {
// Whether the nearest two slot time diff is valid or not?
final int size = batteryHistoryKeys.size();
for (int index = 0; index < size - 1; index++) {
final long currentTime = batteryHistoryKeys.get(index);
final long nextTime = batteryHistoryKeys.get(index + 1);
final long diffTime = Math.abs(
DateUtils.HOUR_IN_MILLIS - Math.abs(currentTime - nextTime));
if (currentTime == 0) {
continue;
} else if (diffTime > VALID_DIFF_DURATION) {
Log.e(TAG, String.format("validateSlotTimestamp() %s > %s",
ConvertUtils.utcToLocalTime(currentTime),
ConvertUtils.utcToLocalTime(nextTime)));
return false;
}
}
return true;
}
} }

View File

@@ -27,6 +27,8 @@ import android.util.Log;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import com.android.settingslib.utils.StringUtil;
import java.time.Duration; import java.time.Duration;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
@@ -254,14 +256,17 @@ public class BatteryDiffEntry {
public String toString() { public String toString() {
final StringBuilder builder = new StringBuilder() final StringBuilder builder = new StringBuilder()
.append("BatteryDiffEntry{") .append("BatteryDiffEntry{")
.append("\n\tname=" + mBatteryHistEntry.mAppLabel) .append("\n\tname=" + getAppLabel())
.append(String.format("\n\tconsume=%.2f%% %f/%f", .append(String.format("\n\tconsume=%.2f%% %f/%f",
mPercentOfTotal, mConsumePower, mTotalConsumePower)) mPercentOfTotal, mConsumePower, mTotalConsumePower))
.append(String.format("\n\tforeground:%d background:%d", .append(String.format("\n\tforeground:%s background:%s",
Duration.ofMillis(mForegroundUsageTimeInMs).getSeconds(), StringUtil.formatElapsedTime(mContext, mForegroundUsageTimeInMs,
Duration.ofMillis(mBackgroundUsageTimeInMs).getSeconds())) /*withSeconds=*/ true, /*collapseTimeUnit=*/ false),
.append(String.format("\n\tpackage:%s uid:%s", StringUtil.formatElapsedTime(mContext, mBackgroundUsageTimeInMs,
mBatteryHistEntry.mPackageName, mBatteryHistEntry.mUid)); /*withSeconds=*/ true, /*collapseTimeUnit=*/ false)))
.append(String.format("\n\tpackage:%s|%s uid:%d userId:%d",
mBatteryHistEntry.mPackageName, getPackageName(),
mBatteryHistEntry.mUid, mBatteryHistEntry.mUserId));
return builder.toString(); return builder.toString();
} }

View File

@@ -123,6 +123,11 @@ public class BatteryHistEntry {
return mConsumerType == ConvertUtils.CONSUMER_TYPE_UID_BATTERY; return mConsumerType == ConvertUtils.CONSUMER_TYPE_UID_BATTERY;
} }
/** Whether this {@link BatteryHistEntry} is system consumer or not. */
public boolean isSystemEntry() {
return mConsumerType == ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY;
}
/** Gets an identifier to represent this {@link BatteryHistEntry}. */ /** Gets an identifier to represent this {@link BatteryHistEntry}. */
public String getKey() { public String getKey() {
if (mKey == null) { if (mKey == null) {

View File

@@ -99,6 +99,13 @@ public class PowerUsageAdvanced extends PowerUsageBase {
return R.xml.power_usage_advanced; return R.xml.power_usage_advanced;
} }
@Override
public void onPause() {
super.onPause();
// Resets the flag to reload usage data in onResume() callback.
mIsChartDataLoaded = false;
}
@Override @Override
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) { protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
refreshFeatureFlag(context); refreshFeatureFlag(context);

View File

@@ -381,6 +381,58 @@ public final class BatteryChartPreferenceControllerTest {
assertThat(pref.getSummary()).isNull(); assertThat(pref.getSummary()).isNull();
} }
@Test
public void testValidateUsageTime_returnTrueIfBatteryDiffEntryIsValid() {
assertThat(BatteryChartPreferenceController.validateUsageTime(
createBatteryDiffEntry(
/*foregroundUsageTimeInMs=*/ DateUtils.MINUTE_IN_MILLIS,
/*backgroundUsageTimeInMs=*/ DateUtils.MINUTE_IN_MILLIS)))
.isTrue();
}
@Test
public void testValidateUsageTime_foregroundTimeExceedThreshold_returnFalse() {
assertThat(BatteryChartPreferenceController.validateUsageTime(
createBatteryDiffEntry(
/*foregroundUsageTimeInMs=*/ DateUtils.HOUR_IN_MILLIS * 3,
/*backgroundUsageTimeInMs=*/ 0)))
.isFalse();
}
@Test
public void testValidateUsageTime_backgroundTimeExceedThreshold_returnFalse() {
assertThat(BatteryChartPreferenceController.validateUsageTime(
createBatteryDiffEntry(
/*foregroundUsageTimeInMs=*/ 0,
/*backgroundUsageTimeInMs=*/ DateUtils.HOUR_IN_MILLIS * 3)))
.isFalse();
}
@Test
public void testValidateSlotTimestamp_emptyContent_returnTrue() {
assertThat(BatteryChartPreferenceController.validateSlotTimestamp(
new ArrayList<Long>())).isTrue();
}
@Test
public void testValidateSlotTimestamp_returnExpectedResult() {
final List<Long> slotTimestampList =
Arrays.asList(
Long.valueOf(0),
Long.valueOf(DateUtils.HOUR_IN_MILLIS),
Long.valueOf(DateUtils.HOUR_IN_MILLIS * 2 + DateUtils.MINUTE_IN_MILLIS),
Long.valueOf(DateUtils.HOUR_IN_MILLIS * 3 + DateUtils.MINUTE_IN_MILLIS * 2));
// Verifies the testing data is correct before we added invalid data into it.
assertThat(BatteryChartPreferenceController.validateSlotTimestamp(slotTimestampList))
.isTrue();
// Insert invalid timestamp into the list.
slotTimestampList.add(
Long.valueOf(DateUtils.HOUR_IN_MILLIS * 4 + DateUtils.MINUTE_IN_MILLIS * 3));
assertThat(BatteryChartPreferenceController.validateSlotTimestamp(slotTimestampList))
.isFalse();
}
private static Map<Long, List<BatteryHistEntry>> createBatteryHistoryMap(int size) { private static Map<Long, List<BatteryHistEntry>> createBatteryHistoryMap(int size) {
final Map<Long, List<BatteryHistEntry>> batteryHistoryMap = new HashMap<>(); final Map<Long, List<BatteryHistEntry>> batteryHistoryMap = new HashMap<>();
for (int index = 0; index < size; index++) { for (int index = 0; index < size; index++) {

View File

@@ -187,6 +187,16 @@ public final class BatteryHistEntryTest {
.isFalse(); .isFalse();
} }
@Test
public void testIsSystemEntry_returnExpectedResult() {
assertThat(createEntry(ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY).isSystemEntry())
.isTrue();
assertThat(createEntry(ConvertUtils.CONSUMER_TYPE_USER_BATTERY).isSystemEntry())
.isFalse();
assertThat(createEntry(ConvertUtils.CONSUMER_TYPE_UID_BATTERY).isSystemEntry())
.isFalse();
}
private static BatteryHistEntry createEntry(int consumerType) { private static BatteryHistEntry createEntry(int consumerType) {
return new BatteryHistEntry(getContentValuesWithType(consumerType)); return new BatteryHistEntry(getContentValuesWithType(consumerType));
} }