Merge "Show app list in Battery Usage page when there is no battery level data." into tm-qpr-dev

This commit is contained in:
TreeHugger Robot
2022-09-21 05:50:45 +00:00
committed by Android (Google) Code Review
6 changed files with 468 additions and 65 deletions

View File

@@ -31,6 +31,7 @@ import android.text.format.DateUtils;
import android.util.Log;
import android.view.View;
import android.view.accessibility.AccessibilityManager;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
@@ -111,6 +112,7 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
private View mCategoryTitleView;
private PreferenceScreen mPreferenceScreen;
private FooterPreference mFooterPreference;
private TextView mChartSummaryTextView;
private BatteryChartViewModel mDailyViewModel;
private List<BatteryChartViewModel> mHourlyViewModels;
@@ -121,9 +123,9 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
private final MetricsFeatureProvider mMetricsFeatureProvider;
private final Handler mHandler = new Handler(Looper.getMainLooper());
private final AnimatorListenerAdapter mHourlyChartFadeInAdapter =
createHourlyChartAnimatorListenerAdapter(/*isToShow=*/ true);
createHourlyChartAnimatorListenerAdapter(/*visible=*/ true);
private final AnimatorListenerAdapter mHourlyChartFadeOutAdapter =
createHourlyChartAnimatorListenerAdapter(/*isToShow=*/ false);
createHourlyChartAnimatorListenerAdapter(/*visible=*/ false);
@VisibleForTesting
final DailyChartLabelTextGenerator mDailyChartLabelTextGenerator =
@@ -289,6 +291,8 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
getTotalHours(batteryLevelData));
if (batteryLevelData == null) {
mDailyChartIndex = BatteryChartViewModel.SELECTED_INDEX_ALL;
mHourlyChartIndex = BatteryChartViewModel.SELECTED_INDEX_ALL;
mDailyViewModel = null;
mHourlyViewModels = null;
refreshUi();
@@ -321,6 +325,11 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
mHandler.post(() -> setBatteryChartViewInner(dailyChartView, hourlyChartView));
animateBatteryChartViewGroup();
}
if (mBatteryChartViewGroup != null) {
final View grandparentView = (View) mBatteryChartViewGroup.getParent();
mChartSummaryTextView = grandparentView != null
? grandparentView.findViewById(R.id.chart_summary) : null;
}
}
private void setBatteryChartViewInner(@NonNull final BatteryChartView dailyChartView,
@@ -367,8 +376,45 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
// Chart views are not initialized.
return false;
}
if (mDailyViewModel == null || mHourlyViewModels == null) {
// Fail to get battery level data, show an empty hourly chart view.
// When mDailyViewModel or mHourlyViewModels is null, there is no battery level data.
// This is mainly in 2 cases:
// 1) battery data is within 2 hours
// 2) no battery data in the latest 7 days (power off >= 7 days)
final boolean refreshUiResult = mDailyViewModel == null || mHourlyViewModels == null
? refreshUiWithNoLevelDataCase()
: refreshUiWithLevelDataCase();
if (!refreshUiResult) {
return false;
}
mHandler.post(() -> {
final long start = System.currentTimeMillis();
removeAndCacheAllPrefs();
addAllPreferences();
refreshCategoryTitle();
Log.d(TAG, String.format("refreshUi is finished in %d/ms",
(System.currentTimeMillis() - start)));
});
return true;
}
private boolean refreshUiWithNoLevelDataCase() {
setChartSummaryVisible(false);
if (mBatteryUsageMap == null) {
// There is no battery level data and battery usage data is not ready, wait for data
// ready to refresh UI. Show nothing temporarily.
mDailyChartView.setVisibility(View.GONE);
mHourlyChartView.setVisibility(View.GONE);
mDailyChartView.setViewModel(null);
mHourlyChartView.setViewModel(null);
return false;
} else if (mBatteryUsageMap
.get(BatteryChartViewModel.SELECTED_INDEX_ALL)
.get(BatteryChartViewModel.SELECTED_INDEX_ALL) == null) {
// There is no battery level data and battery usage data, show an empty hourly chart
// view.
mDailyChartView.setVisibility(View.GONE);
mHourlyChartView.setVisibility(View.VISIBLE);
mHourlyChartView.setViewModel(null);
@@ -376,7 +422,12 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
addFooterPreferenceIfNeeded(false);
return false;
}
return true;
}
private boolean refreshUiWithLevelDataCase() {
setChartSummaryVisible(true);
// Gets valid battery level data.
if (isBatteryLevelDataInOneDay()) {
// Only 1 day data, hide the daily chart view.
mDailyChartView.setVisibility(View.GONE);
@@ -389,10 +440,11 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
if (mDailyChartIndex == BatteryChartViewModel.SELECTED_INDEX_ALL) {
// Multiple days are selected, hide the hourly chart view.
animateBatteryHourlyChartView(/*isToShow=*/ false);
animateBatteryHourlyChartView(/*visible=*/ false);
} else {
animateBatteryHourlyChartView(/*isToShow=*/ true);
final BatteryChartViewModel hourlyViewModel = mHourlyViewModels.get(mDailyChartIndex);
animateBatteryHourlyChartView(/*visible=*/ true);
final BatteryChartViewModel hourlyViewModel =
mHourlyViewModels.get(mDailyChartIndex);
hourlyViewModel.setSelectedIndex(mHourlyChartIndex);
mHourlyChartView.setViewModel(hourlyViewModel);
}
@@ -401,14 +453,6 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
// Battery usage data is not ready, wait for data ready to refresh UI.
return false;
}
mHandler.post(() -> {
final long start = System.currentTimeMillis();
removeAndCacheAllPrefs();
addAllPreferences();
refreshCategoryTitle();
Log.d(TAG, String.format("refreshUi is finished in %d/ms",
(System.currentTimeMillis() - start)));
});
return true;
}
@@ -427,7 +471,7 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
if (!batteryDiffData.getAppDiffEntryList().isEmpty()) {
addPreferenceToScreen(batteryDiffData.getAppDiffEntryList());
}
// Adds the expabable divider if we have system entries data.
// Adds the expandable divider if we have system entries data.
if (!batteryDiffData.getSystemDiffEntryList().isEmpty()) {
if (mExpandDividerPreference == null) {
mExpandDividerPreference = new ExpandDividerPreference(mPrefContext);
@@ -645,12 +689,12 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
}
}
private void animateBatteryHourlyChartView(final boolean isToShow) {
private void animateBatteryHourlyChartView(final boolean visible) {
if (mHourlyChartView == null) {
return;
}
if (isToShow) {
if (visible) {
mHourlyChartView.setAlpha(0f);
mHourlyChartView.setVisibility(View.VISIBLE);
mHourlyChartView.animate()
@@ -667,9 +711,15 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
}
}
private void setChartSummaryVisible(final boolean visible) {
if (mChartSummaryTextView != null) {
mChartSummaryTextView.setVisibility(visible ? View.VISIBLE : View.GONE);
}
}
private AnimatorListenerAdapter createHourlyChartAnimatorListenerAdapter(
final boolean isToShow) {
final int visibility = isToShow ? View.VISIBLE : View.GONE;
final boolean visible) {
final int visibility = visible ? View.VISIBLE : View.GONE;
return new AnimatorListenerAdapter() {
@Override

View File

@@ -55,11 +55,6 @@ public final class ConvertUtils {
// 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;
// Keys for metric metadata.
static final int METRIC_KEY_PACKAGE = 1;
static final int METRIC_KEY_BATTERY_LEVEL = 2;
static final int METRIC_KEY_BATTERY_USAGE = 3;
@VisibleForTesting
static double PERCENTAGE_OF_TOTAL_THRESHOLD = 1f;
@@ -87,7 +82,7 @@ public final class ConvertUtils {
}
/** Converts to content values */
public static ContentValues convert(
public static ContentValues convertToContentValues(
BatteryEntry entry,
BatteryUsageStats batteryUsageStats,
int batteryLevel,
@@ -130,6 +125,21 @@ public final class ConvertUtils {
return values;
}
/** Converts to {@link BatteryHistEntry} */
public static BatteryHistEntry convertToBatteryHistEntry(
BatteryEntry entry,
BatteryUsageStats batteryUsageStats) {
return new BatteryHistEntry(
convertToContentValues(
entry,
batteryUsageStats,
/*batteryLevel=*/ 0,
/*batteryStatus=*/ 0,
/*batteryHealth=*/ 0,
/*bootTimestamp=*/ 0,
/*timestamp=*/ 0));
}
/** Converts UTC timestamp to human readable local time string. */
public static String utcToLocalTime(Context context, long timestamp) {
final Locale locale = getLocale(context);

View File

@@ -22,6 +22,9 @@ import android.app.settings.SettingsEnums;
import android.content.ContentValues;
import android.content.Context;
import android.os.AsyncTask;
import android.os.BatteryStatsManager;
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
import android.os.Handler;
import android.os.Looper;
import android.os.UserHandle;
@@ -50,6 +53,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/**
* A utility class to process data loaded from database and make the data easy to use for battery
@@ -97,7 +101,9 @@ public final class DataProcessor {
@Nullable final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap,
final UsageMapAsyncResponse asyncResponseDelegate) {
if (batteryHistoryMap == null || batteryHistoryMap.isEmpty()) {
Log.d(TAG, "getBatteryLevelData() returns null");
Log.d(TAG, "batteryHistoryMap is null in getBatteryLevelData()");
loadBatteryUsageDataFromBatteryStatsService(
context, handler, asyncResponseDelegate);
return null;
}
handler = handler != null ? handler : new Handler(Looper.getMainLooper());
@@ -107,16 +113,20 @@ public final class DataProcessor {
// Wrap and processed history map into easy-to-use format for UI rendering.
final BatteryLevelData batteryLevelData =
getLevelDataThroughProcessedHistoryMap(context, processedBatteryHistoryMap);
if (batteryLevelData == null) {
loadBatteryUsageDataFromBatteryStatsService(
context, handler, asyncResponseDelegate);
Log.d(TAG, "getBatteryLevelData() returns null");
return null;
}
// 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();
}
new ComputeUsageMapAndLoadItemsTask(
context,
handler,
asyncResponseDelegate,
batteryLevelData.getHourlyBatteryLevelsPerDay(),
processedBatteryHistoryMap).execute();
return batteryLevelData;
}
@@ -365,19 +375,165 @@ public final class DataProcessor {
return null;
}
final MetricsFeatureProvider metricsFeatureProvider =
FeatureFactory.getFactory(context).getMetricsFeatureProvider();
metricsFeatureProvider.action(
context,
SettingsEnums.ACTION_BATTERY_USAGE_SHOWN_APP_COUNT,
countOfAppAfterPurge);
metricsFeatureProvider.action(
context,
SettingsEnums.ACTION_BATTERY_USAGE_HIDDEN_APP_COUNT,
countOfAppBeforePurge - countOfAppAfterPurge);
logAppCountMetrics(context, countOfAppBeforePurge, countOfAppAfterPurge);
return resultMap;
}
@VisibleForTesting
@Nullable
static BatteryDiffData generateBatteryDiffData(
final Context context,
@Nullable final List<BatteryEntry> batteryEntryList,
final BatteryUsageStats batteryUsageStats) {
final List<BatteryHistEntry> batteryHistEntryList =
convertToBatteryHistEntry(batteryEntryList, batteryUsageStats);
if (batteryHistEntryList == null || batteryHistEntryList.isEmpty()) {
Log.w(TAG, "batteryHistEntryList is null or empty in generateBatteryDiffData()");
return null;
}
final int currentUserId = context.getUserId();
final UserHandle userHandle =
Utils.getManagedProfile(context.getSystemService(UserManager.class));
final int workProfileUserId =
userHandle != null ? userHandle.getIdentifier() : Integer.MIN_VALUE;
final List<BatteryDiffEntry> appEntries = new ArrayList<>();
final List<BatteryDiffEntry> systemEntries = new ArrayList<>();
double totalConsumePower = 0f;
double consumePowerFromOtherUsers = 0f;
for (BatteryHistEntry entry : batteryHistEntryList) {
final boolean isFromOtherUsers = isConsumedFromOtherUsers(
currentUserId, workProfileUserId, entry);
totalConsumePower += entry.mConsumePower;
if (isFromOtherUsers) {
consumePowerFromOtherUsers += entry.mConsumePower;
} else {
final BatteryDiffEntry currentBatteryDiffEntry = new BatteryDiffEntry(
context,
entry.mForegroundUsageTimeInMs,
entry.mBackgroundUsageTimeInMs,
entry.mConsumePower,
entry);
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;
}
return new BatteryDiffData(appEntries, systemEntries, totalConsumePower);
}
/**
* Starts the async task to load battery diff usage data and load app labels + icons.
*/
private static void loadBatteryUsageDataFromBatteryStatsService(
Context context,
@Nullable Handler handler,
final UsageMapAsyncResponse asyncResponseDelegate) {
new LoadUsageMapFromBatteryStatsServiceTask(
context,
handler,
asyncResponseDelegate).execute();
}
/**
* @return Returns the overall battery usage data from battery stats service directly.
*
* The returned value should be always a 2d map and composed by only 1 part:
* - [SELECTED_INDEX_ALL][SELECTED_INDEX_ALL]
*/
@Nullable
private static Map<Integer, Map<Integer, BatteryDiffData>> getBatteryUsageMapFromStatsService(
final Context context) {
final Map<Integer, Map<Integer, BatteryDiffData>> resultMap = new HashMap<>();
final Map<Integer, BatteryDiffData> allUsageMap = new HashMap<>();
// Always construct the map whether the value is null or not.
allUsageMap.put(SELECTED_INDEX_ALL,
getBatteryDiffDataFromBatteryStatsService(context));
resultMap.put(SELECTED_INDEX_ALL, allUsageMap);
// Compute the apps number before purge. Must put before purgeLowPercentageAndFakeData.
final int countOfAppBeforePurge = getCountOfApps(resultMap);
purgeLowPercentageAndFakeData(context, resultMap);
// Compute the apps number after purge. Must put after purgeLowPercentageAndFakeData.
final int countOfAppAfterPurge = getCountOfApps(resultMap);
logAppCountMetrics(context, countOfAppBeforePurge, countOfAppAfterPurge);
return resultMap;
}
@Nullable
private static BatteryDiffData getBatteryDiffDataFromBatteryStatsService(
final Context context) {
BatteryDiffData batteryDiffData = null;
try {
final BatteryUsageStatsQuery batteryUsageStatsQuery =
new BatteryUsageStatsQuery.Builder().includeBatteryHistory().build();
final BatteryUsageStats batteryUsageStats =
context.getSystemService(BatteryStatsManager.class)
.getBatteryUsageStats(batteryUsageStatsQuery);
if (batteryUsageStats == null) {
Log.w(TAG, "batteryUsageStats is null content");
return null;
}
final List<BatteryEntry> batteryEntryList =
generateBatteryEntryListFromBatteryUsageStats(context, batteryUsageStats);
batteryDiffData = generateBatteryDiffData(context, batteryEntryList, batteryUsageStats);
} catch (RuntimeException e) {
Log.e(TAG, "load batteryUsageStats:" + e);
}
return batteryDiffData;
}
@Nullable
private static List<BatteryEntry> generateBatteryEntryListFromBatteryUsageStats(
final Context context, final BatteryUsageStats batteryUsageStats) {
// Loads the battery consuming data.
final BatteryAppListPreferenceController controller =
new BatteryAppListPreferenceController(
context,
/*preferenceKey=*/ null,
/*lifecycle=*/ null,
/*activity*=*/ null,
/*fragment=*/ null);
return controller.getBatteryEntryList(batteryUsageStats, /*showAllApps=*/ true);
}
@Nullable
private static List<BatteryHistEntry> convertToBatteryHistEntry(
@Nullable final List<BatteryEntry> batteryEntryList,
final BatteryUsageStats batteryUsageStats) {
if (batteryEntryList == null || batteryEntryList.isEmpty()) {
Log.w(TAG, "batteryEntryList is null or empty in convertToBatteryHistEntry()");
return null;
}
return batteryEntryList.stream()
.filter(entry -> {
final long foregroundMs = entry.getTimeInForegroundMs();
final long backgroundMs = entry.getTimeInBackgroundMs();
return entry.getConsumedPower() > 0
|| (entry.getConsumedPower() == 0
&& (foregroundMs != 0 || backgroundMs != 0));
})
.map(entry -> ConvertUtils.convertToBatteryHistEntry(
entry,
batteryUsageStats))
.collect(Collectors.toList());
}
/**
* Interpolates history map based on expected timestamp slots and processes the corner case when
* the expected start timestamp is earlier than what we have.
@@ -940,6 +1096,22 @@ public final class DataProcessor {
return true;
}
private static void loadLabelAndIcon(
@Nullable final Map<Integer, Map<Integer, BatteryDiffData>> batteryUsageMap) {
if (batteryUsageMap == null) {
return;
}
// 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());
}
}
private static long getTimestampWithDayDiff(final long timestamp, final int dayDiff) {
final Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(timestamp);
@@ -1006,6 +1178,21 @@ public final class DataProcessor {
return batteryDiffEntry;
}
private static void logAppCountMetrics(
Context context, final int countOfAppBeforePurge, final int countOfAppAfterPurge) {
context = context.getApplicationContext();
final MetricsFeatureProvider metricsFeatureProvider =
FeatureFactory.getFactory(context).getMetricsFeatureProvider();
metricsFeatureProvider.action(
context,
SettingsEnums.ACTION_BATTERY_USAGE_SHOWN_APP_COUNT,
countOfAppAfterPurge);
metricsFeatureProvider.action(
context,
SettingsEnums.ACTION_BATTERY_USAGE_HIDDEN_APP_COUNT,
countOfAppBeforePurge - countOfAppAfterPurge);
}
private static void log(Context context, final String content, final long timestamp,
final BatteryHistEntry entry) {
if (DEBUG) {
@@ -1015,12 +1202,12 @@ public final class DataProcessor {
}
// Compute diff map and loads all items (icon and label) in the background.
private static final class ComputeUsageMapAndLoadItemsTask
private static class ComputeUsageMapAndLoadItemsTask
extends AsyncTask<Void, Void, Map<Integer, Map<Integer, BatteryDiffData>>> {
private Context mApplicationContext;
private Handler mHandler;
private UsageMapAsyncResponse mAsyncResponseDelegate;
Context mApplicationContext;
final Handler mHandler;
final UsageMapAsyncResponse mAsyncResponseDelegate;
private List<BatteryLevelData.PeriodBatteryLevelData> mHourlyBatteryLevelsPerDay;
private Map<Long, Map<String, BatteryHistEntry>> mBatteryHistoryMap;
@@ -1051,17 +1238,7 @@ public final class DataProcessor {
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());
}
}
loadLabelAndIcon(batteryUsageMap);
Log.d(TAG, String.format("execute ComputeUsageMapAndLoadItemsTask in %d/ms",
(System.currentTimeMillis() - startTime)));
return batteryUsageMap;
@@ -1081,4 +1258,35 @@ public final class DataProcessor {
}
}
}
// Loads battery usage data from battery stats service directly and loads all items (icon and
// label) in the background.
private static final class LoadUsageMapFromBatteryStatsServiceTask
extends ComputeUsageMapAndLoadItemsTask {
private LoadUsageMapFromBatteryStatsServiceTask(
Context context,
Handler handler,
final UsageMapAsyncResponse asyncResponseDelegate) {
super(context, handler, asyncResponseDelegate, /*hourlyBatteryLevelsPerDay=*/ null,
/*batteryHistoryMap=*/ null);
}
@Override
protected Map<Integer, Map<Integer, BatteryDiffData>> doInBackground(Void... voids) {
if (mApplicationContext == null
|| mHandler == null
|| mAsyncResponseDelegate == null) {
Log.e(TAG, "invalid input for ComputeUsageMapAndLoadItemsTask()");
return null;
}
final long startTime = System.currentTimeMillis();
final Map<Integer, Map<Integer, BatteryDiffData>> batteryUsageMap =
getBatteryUsageMapFromStatsService(mApplicationContext);
loadLabelAndIcon(batteryUsageMap);
Log.d(TAG, String.format("execute LoadUsageMapFromBatteryStatsServiceTask in %d/ms",
(System.currentTimeMillis() - startTime)));
return batteryUsageMap;
}
}
}

View File

@@ -64,7 +64,7 @@ public final class BatteryHistEntryTest {
when(mMockBatteryEntry.getConsumerType())
.thenReturn(ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY);
final ContentValues values =
ConvertUtils.convert(
ConvertUtils.convertToContentValues(
mMockBatteryEntry,
mBatteryUsageStats,
/*batteryLevel=*/ 12,

View File

@@ -71,7 +71,7 @@ public final class ConvertUtilsTest {
}
@Test
public void convert_returnsExpectedContentValues() {
public void convertToContentValues_returnsExpectedContentValues() {
final int expectedType = 3;
when(mMockBatteryEntry.getUid()).thenReturn(1001);
when(mMockBatteryEntry.getLabel()).thenReturn("Settings");
@@ -88,7 +88,7 @@ public final class ConvertUtilsTest {
.thenReturn(ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY);
final ContentValues values =
ConvertUtils.convert(
ConvertUtils.convertToContentValues(
mMockBatteryEntry,
mBatteryUsageStats,
/*batteryLevel=*/ 12,
@@ -128,9 +128,9 @@ public final class ConvertUtilsTest {
}
@Test
public void convert_nullBatteryEntry_returnsExpectedContentValues() {
public void convertToContentValues_nullBatteryEntry_returnsExpectedContentValues() {
final ContentValues values =
ConvertUtils.convert(
ConvertUtils.convertToContentValues(
/*entry=*/ null,
/*batteryUsageStats=*/ null,
/*batteryLevel=*/ 12,
@@ -154,6 +154,76 @@ public final class ConvertUtilsTest {
.isEqualTo(ConvertUtils.FAKE_PACKAGE_NAME);
}
@Test
public void convertToBatteryHistEntry_returnsExpectedResult() {
final int expectedType = 3;
when(mMockBatteryEntry.getUid()).thenReturn(1001);
when(mMockBatteryEntry.getLabel()).thenReturn("Settings");
when(mMockBatteryEntry.getDefaultPackageName())
.thenReturn("com.android.settings.battery");
when(mMockBatteryEntry.isHidden()).thenReturn(true);
when(mBatteryUsageStats.getConsumedPower()).thenReturn(5.1);
when(mMockBatteryEntry.getConsumedPower()).thenReturn(1.1);
mMockBatteryEntry.mPercent = 0.3;
when(mMockBatteryEntry.getTimeInForegroundMs()).thenReturn(1234L);
when(mMockBatteryEntry.getTimeInBackgroundMs()).thenReturn(5689L);
when(mMockBatteryEntry.getPowerComponentId()).thenReturn(expectedType);
when(mMockBatteryEntry.getConsumerType())
.thenReturn(ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY);
final BatteryHistEntry batteryHistEntry =
ConvertUtils.convertToBatteryHistEntry(
mMockBatteryEntry,
mBatteryUsageStats);
assertThat(batteryHistEntry.mUid).isEqualTo(1001L);
assertThat(batteryHistEntry.mUserId)
.isEqualTo(UserHandle.getUserId(1001));
assertThat(batteryHistEntry.mAppLabel)
.isEqualTo("Settings");
assertThat(batteryHistEntry.mPackageName)
.isEqualTo("com.android.settings.battery");
assertThat(batteryHistEntry.mIsHidden).isTrue();
assertThat(batteryHistEntry.mBootTimestamp)
.isEqualTo(0L);
assertThat(batteryHistEntry.mTimestamp).isEqualTo(0L);
assertThat(batteryHistEntry.mZoneId)
.isEqualTo(TimeZone.getDefault().getID());
assertThat(batteryHistEntry.mTotalPower).isEqualTo(5.1);
assertThat(batteryHistEntry.mConsumePower).isEqualTo(1.1);
assertThat(batteryHistEntry.mPercentOfTotal).isEqualTo(0.3);
assertThat(batteryHistEntry.mForegroundUsageTimeInMs)
.isEqualTo(1234L);
assertThat(batteryHistEntry.mBackgroundUsageTimeInMs)
.isEqualTo(5689L);
assertThat(batteryHistEntry.mDrainType).isEqualTo(expectedType);
assertThat(batteryHistEntry.mConsumerType)
.isEqualTo(ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY);
assertThat(batteryHistEntry.mBatteryLevel).isEqualTo(0);
assertThat(batteryHistEntry.mBatteryStatus).isEqualTo(0);
assertThat(batteryHistEntry.mBatteryHealth).isEqualTo(0);
}
@Test
public void convertToBatteryHistEntry_nullBatteryEntry_returnsExpectedResult() {
final BatteryHistEntry batteryHistEntry =
ConvertUtils.convertToBatteryHistEntry(
/*entry=*/ null,
/*batteryUsageStats=*/ null);
assertThat(batteryHistEntry.mBootTimestamp)
.isEqualTo(0L);
assertThat(batteryHistEntry.mTimestamp)
.isEqualTo(0);
assertThat(batteryHistEntry.mZoneId)
.isEqualTo(TimeZone.getDefault().getID());
assertThat(batteryHistEntry.mBatteryLevel).isEqualTo(0);
assertThat(batteryHistEntry.mBatteryStatus).isEqualTo(0);
assertThat(batteryHistEntry.mBatteryHealth).isEqualTo(0);
assertThat(batteryHistEntry.mPackageName)
.isEqualTo(ConvertUtils.FAKE_PACKAGE_NAME);
}
@Test
public void getIndexedUsageMap_nullOrEmptyHistoryMap_returnEmptyCollection() {
final int timeSlotSize = 2;

View File

@@ -18,6 +18,7 @@ package com.android.settings.fuelgauge.batteryusage;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
@@ -26,6 +27,8 @@ import static org.mockito.Mockito.when;
import android.app.settings.SettingsEnums;
import android.content.ContentValues;
import android.content.Context;
import android.os.BatteryConsumer;
import android.os.BatteryUsageStats;
import android.text.format.DateUtils;
import com.android.settings.fuelgauge.BatteryUtils;
@@ -36,6 +39,7 @@ import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
@@ -59,6 +63,13 @@ public class DataProcessorTest {
private MetricsFeatureProvider mMetricsFeatureProvider;
private PowerUsageFeatureProvider mPowerUsageFeatureProvider;
@Mock private BatteryUsageStats mBatteryUsageStats;
@Mock private BatteryEntry mMockBatteryEntry1;
@Mock private BatteryEntry mMockBatteryEntry2;
@Mock private BatteryEntry mMockBatteryEntry3;
@Mock private BatteryEntry mMockBatteryEntry4;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
@@ -883,6 +894,60 @@ public class DataProcessorTest {
.action(mContext, SettingsEnums.ACTION_BATTERY_USAGE_HIDDEN_APP_COUNT, 0);
}
@Test
public void generateBatteryDiffData_emptyBatteryEntryList_returnNull() {
assertThat(DataProcessor.generateBatteryDiffData(
mContext, null, mBatteryUsageStats)).isNull();
}
@Test
public void generateBatteryDiffData_returnsExpectedResult() {
final List<BatteryEntry> batteryEntryList = new ArrayList<>();
batteryEntryList.add(mMockBatteryEntry1);
batteryEntryList.add(mMockBatteryEntry2);
batteryEntryList.add(mMockBatteryEntry3);
batteryEntryList.add(mMockBatteryEntry4);
doReturn(0.0).when(mMockBatteryEntry1).getConsumedPower();
doReturn(30L).when(mMockBatteryEntry1).getTimeInForegroundMs();
doReturn(40L).when(mMockBatteryEntry1).getTimeInBackgroundMs();
doReturn(1).when(mMockBatteryEntry1).getUid();
doReturn(ConvertUtils.CONSUMER_TYPE_UID_BATTERY).when(mMockBatteryEntry1).getConsumerType();
doReturn(0.5).when(mMockBatteryEntry2).getConsumedPower();
doReturn(20L).when(mMockBatteryEntry2).getTimeInForegroundMs();
doReturn(20L).when(mMockBatteryEntry2).getTimeInBackgroundMs();
doReturn(2).when(mMockBatteryEntry2).getUid();
doReturn(ConvertUtils.CONSUMER_TYPE_UID_BATTERY).when(mMockBatteryEntry2).getConsumerType();
doReturn(0.0).when(mMockBatteryEntry3).getConsumedPower();
doReturn(0L).when(mMockBatteryEntry3).getTimeInForegroundMs();
doReturn(0L).when(mMockBatteryEntry3).getTimeInBackgroundMs();
doReturn(3).when(mMockBatteryEntry3).getUid();
doReturn(ConvertUtils.CONSUMER_TYPE_UID_BATTERY).when(mMockBatteryEntry3).getConsumerType();
doReturn(1.5).when(mMockBatteryEntry4).getConsumedPower();
doReturn(10L).when(mMockBatteryEntry4).getTimeInForegroundMs();
doReturn(10L).when(mMockBatteryEntry4).getTimeInBackgroundMs();
doReturn(4).when(mMockBatteryEntry4).getUid();
doReturn(ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY)
.when(mMockBatteryEntry4).getConsumerType();
doReturn(BatteryConsumer.POWER_COMPONENT_CAMERA)
.when(mMockBatteryEntry4).getPowerComponentId();
final BatteryDiffData batteryDiffData = DataProcessor.generateBatteryDiffData(
mContext, batteryEntryList, mBatteryUsageStats);
assertBatteryDiffEntry(
batteryDiffData.getAppDiffEntryList().get(0), 0, /*uid=*/ 2L,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*consumePercentage=*/ 25.0,
/*foregroundUsageTimeInMs=*/ 20, /*backgroundUsageTimeInMs=*/ 20);
assertBatteryDiffEntry(
batteryDiffData.getAppDiffEntryList().get(1), 0, /*uid=*/ 1L,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*consumePercentage=*/ 0.0,
/*foregroundUsageTimeInMs=*/ 30, /*backgroundUsageTimeInMs=*/ 40);
assertBatteryDiffEntry(
batteryDiffData.getSystemDiffEntryList().get(0), 0, /*uid=*/ 4L,
ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY, /*consumePercentage=*/ 75.0,
/*foregroundUsageTimeInMs=*/ 10, /*backgroundUsageTimeInMs=*/ 10);
}
private static Map<Long, Map<String, BatteryHistEntry>> createHistoryMap(
final long[] timestamps, final int[] levels) {
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap = new HashMap<>();