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:
@@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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) {
|
||||||
|
@@ -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);
|
||||||
|
@@ -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++) {
|
||||||
|
@@ -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));
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user