Show app list in Battery Usage page when there is no battery level data.
https://drive.google.com/file/d/1iQxKmEp_weNYdT4JnsiZgYy9mCdn7SRv/view?usp=sharing Bug: 246233366 Test: make RunSettingsRoboTests + manually Change-Id: If536c93652506c8009f5cabf3d0ae373b6825bfc Merged-In: If536c93652506c8009f5cabf3d0ae373b6825bfc
This commit is contained in:
@@ -31,6 +31,7 @@ import android.text.format.DateUtils;
|
|||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.accessibility.AccessibilityManager;
|
import android.view.accessibility.AccessibilityManager;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.VisibleForTesting;
|
import androidx.annotation.VisibleForTesting;
|
||||||
@@ -111,6 +112,7 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|
|||||||
private View mCategoryTitleView;
|
private View mCategoryTitleView;
|
||||||
private PreferenceScreen mPreferenceScreen;
|
private PreferenceScreen mPreferenceScreen;
|
||||||
private FooterPreference mFooterPreference;
|
private FooterPreference mFooterPreference;
|
||||||
|
private TextView mChartSummaryTextView;
|
||||||
private BatteryChartViewModel mDailyViewModel;
|
private BatteryChartViewModel mDailyViewModel;
|
||||||
private List<BatteryChartViewModel> mHourlyViewModels;
|
private List<BatteryChartViewModel> mHourlyViewModels;
|
||||||
|
|
||||||
@@ -121,9 +123,9 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|
|||||||
private final MetricsFeatureProvider mMetricsFeatureProvider;
|
private final MetricsFeatureProvider mMetricsFeatureProvider;
|
||||||
private final Handler mHandler = new Handler(Looper.getMainLooper());
|
private final Handler mHandler = new Handler(Looper.getMainLooper());
|
||||||
private final AnimatorListenerAdapter mHourlyChartFadeInAdapter =
|
private final AnimatorListenerAdapter mHourlyChartFadeInAdapter =
|
||||||
createHourlyChartAnimatorListenerAdapter(/*isToShow=*/ true);
|
createHourlyChartAnimatorListenerAdapter(/*visible=*/ true);
|
||||||
private final AnimatorListenerAdapter mHourlyChartFadeOutAdapter =
|
private final AnimatorListenerAdapter mHourlyChartFadeOutAdapter =
|
||||||
createHourlyChartAnimatorListenerAdapter(/*isToShow=*/ false);
|
createHourlyChartAnimatorListenerAdapter(/*visible=*/ false);
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
final DailyChartLabelTextGenerator mDailyChartLabelTextGenerator =
|
final DailyChartLabelTextGenerator mDailyChartLabelTextGenerator =
|
||||||
@@ -289,6 +291,8 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|
|||||||
getTotalHours(batteryLevelData));
|
getTotalHours(batteryLevelData));
|
||||||
|
|
||||||
if (batteryLevelData == null) {
|
if (batteryLevelData == null) {
|
||||||
|
mDailyChartIndex = BatteryChartViewModel.SELECTED_INDEX_ALL;
|
||||||
|
mHourlyChartIndex = BatteryChartViewModel.SELECTED_INDEX_ALL;
|
||||||
mDailyViewModel = null;
|
mDailyViewModel = null;
|
||||||
mHourlyViewModels = null;
|
mHourlyViewModels = null;
|
||||||
refreshUi();
|
refreshUi();
|
||||||
@@ -321,6 +325,11 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|
|||||||
mHandler.post(() -> setBatteryChartViewInner(dailyChartView, hourlyChartView));
|
mHandler.post(() -> setBatteryChartViewInner(dailyChartView, hourlyChartView));
|
||||||
animateBatteryChartViewGroup();
|
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,
|
private void setBatteryChartViewInner(@NonNull final BatteryChartView dailyChartView,
|
||||||
@@ -367,8 +376,45 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|
|||||||
// Chart views are not initialized.
|
// Chart views are not initialized.
|
||||||
return false;
|
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);
|
mDailyChartView.setVisibility(View.GONE);
|
||||||
mHourlyChartView.setVisibility(View.VISIBLE);
|
mHourlyChartView.setVisibility(View.VISIBLE);
|
||||||
mHourlyChartView.setViewModel(null);
|
mHourlyChartView.setViewModel(null);
|
||||||
@@ -376,7 +422,12 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|
|||||||
addFooterPreferenceIfNeeded(false);
|
addFooterPreferenceIfNeeded(false);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean refreshUiWithLevelDataCase() {
|
||||||
|
setChartSummaryVisible(true);
|
||||||
|
// Gets valid battery level data.
|
||||||
if (isBatteryLevelDataInOneDay()) {
|
if (isBatteryLevelDataInOneDay()) {
|
||||||
// Only 1 day data, hide the daily chart view.
|
// Only 1 day data, hide the daily chart view.
|
||||||
mDailyChartView.setVisibility(View.GONE);
|
mDailyChartView.setVisibility(View.GONE);
|
||||||
@@ -389,10 +440,11 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|
|||||||
|
|
||||||
if (mDailyChartIndex == BatteryChartViewModel.SELECTED_INDEX_ALL) {
|
if (mDailyChartIndex == BatteryChartViewModel.SELECTED_INDEX_ALL) {
|
||||||
// Multiple days are selected, hide the hourly chart view.
|
// Multiple days are selected, hide the hourly chart view.
|
||||||
animateBatteryHourlyChartView(/*isToShow=*/ false);
|
animateBatteryHourlyChartView(/*visible=*/ false);
|
||||||
} else {
|
} else {
|
||||||
animateBatteryHourlyChartView(/*isToShow=*/ true);
|
animateBatteryHourlyChartView(/*visible=*/ true);
|
||||||
final BatteryChartViewModel hourlyViewModel = mHourlyViewModels.get(mDailyChartIndex);
|
final BatteryChartViewModel hourlyViewModel =
|
||||||
|
mHourlyViewModels.get(mDailyChartIndex);
|
||||||
hourlyViewModel.setSelectedIndex(mHourlyChartIndex);
|
hourlyViewModel.setSelectedIndex(mHourlyChartIndex);
|
||||||
mHourlyChartView.setViewModel(hourlyViewModel);
|
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.
|
// Battery usage data is not ready, wait for data ready to refresh UI.
|
||||||
return false;
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -427,7 +471,7 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|
|||||||
if (!batteryDiffData.getAppDiffEntryList().isEmpty()) {
|
if (!batteryDiffData.getAppDiffEntryList().isEmpty()) {
|
||||||
addPreferenceToScreen(batteryDiffData.getAppDiffEntryList());
|
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 (!batteryDiffData.getSystemDiffEntryList().isEmpty()) {
|
||||||
if (mExpandDividerPreference == null) {
|
if (mExpandDividerPreference == null) {
|
||||||
mExpandDividerPreference = new ExpandDividerPreference(mPrefContext);
|
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) {
|
if (mHourlyChartView == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isToShow) {
|
if (visible) {
|
||||||
mHourlyChartView.setAlpha(0f);
|
mHourlyChartView.setAlpha(0f);
|
||||||
mHourlyChartView.setVisibility(View.VISIBLE);
|
mHourlyChartView.setVisibility(View.VISIBLE);
|
||||||
mHourlyChartView.animate()
|
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(
|
private AnimatorListenerAdapter createHourlyChartAnimatorListenerAdapter(
|
||||||
final boolean isToShow) {
|
final boolean visible) {
|
||||||
final int visibility = isToShow ? View.VISIBLE : View.GONE;
|
final int visibility = visible ? View.VISIBLE : View.GONE;
|
||||||
|
|
||||||
return new AnimatorListenerAdapter() {
|
return new AnimatorListenerAdapter() {
|
||||||
@Override
|
@Override
|
||||||
|
@@ -55,11 +55,6 @@ public final class ConvertUtils {
|
|||||||
// Maximum total time value for each slot cumulative data at most 2 hours.
|
// 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;
|
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
|
@VisibleForTesting
|
||||||
static double PERCENTAGE_OF_TOTAL_THRESHOLD = 1f;
|
static double PERCENTAGE_OF_TOTAL_THRESHOLD = 1f;
|
||||||
|
|
||||||
@@ -87,7 +82,7 @@ public final class ConvertUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Converts to content values */
|
/** Converts to content values */
|
||||||
public static ContentValues convert(
|
public static ContentValues convertToContentValues(
|
||||||
BatteryEntry entry,
|
BatteryEntry entry,
|
||||||
BatteryUsageStats batteryUsageStats,
|
BatteryUsageStats batteryUsageStats,
|
||||||
int batteryLevel,
|
int batteryLevel,
|
||||||
@@ -130,6 +125,21 @@ public final class ConvertUtils {
|
|||||||
return values;
|
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. */
|
/** Converts UTC timestamp to human readable local time string. */
|
||||||
public static String utcToLocalTime(Context context, long timestamp) {
|
public static String utcToLocalTime(Context context, long timestamp) {
|
||||||
final Locale locale = getLocale(context);
|
final Locale locale = getLocale(context);
|
||||||
|
@@ -22,6 +22,9 @@ import android.app.settings.SettingsEnums;
|
|||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
|
import android.os.BatteryStatsManager;
|
||||||
|
import android.os.BatteryUsageStats;
|
||||||
|
import android.os.BatteryUsageStatsQuery;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
@@ -50,6 +53,7 @@ import java.util.Iterator;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
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
|
* 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,
|
@Nullable final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap,
|
||||||
final UsageMapAsyncResponse asyncResponseDelegate) {
|
final UsageMapAsyncResponse asyncResponseDelegate) {
|
||||||
if (batteryHistoryMap == null || batteryHistoryMap.isEmpty()) {
|
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;
|
return null;
|
||||||
}
|
}
|
||||||
handler = handler != null ? handler : new Handler(Looper.getMainLooper());
|
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.
|
// Wrap and processed history map into easy-to-use format for UI rendering.
|
||||||
final BatteryLevelData batteryLevelData =
|
final BatteryLevelData batteryLevelData =
|
||||||
getLevelDataThroughProcessedHistoryMap(context, processedBatteryHistoryMap);
|
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.
|
// Start the async task to compute diff usage data and load labels and icons.
|
||||||
if (batteryLevelData != null) {
|
|
||||||
new ComputeUsageMapAndLoadItemsTask(
|
new ComputeUsageMapAndLoadItemsTask(
|
||||||
context,
|
context,
|
||||||
handler,
|
handler,
|
||||||
asyncResponseDelegate,
|
asyncResponseDelegate,
|
||||||
batteryLevelData.getHourlyBatteryLevelsPerDay(),
|
batteryLevelData.getHourlyBatteryLevelsPerDay(),
|
||||||
processedBatteryHistoryMap).execute();
|
processedBatteryHistoryMap).execute();
|
||||||
}
|
|
||||||
|
|
||||||
return batteryLevelData;
|
return batteryLevelData;
|
||||||
}
|
}
|
||||||
@@ -365,19 +375,165 @@ public final class DataProcessor {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
final MetricsFeatureProvider metricsFeatureProvider =
|
logAppCountMetrics(context, countOfAppBeforePurge, countOfAppAfterPurge);
|
||||||
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);
|
|
||||||
return resultMap;
|
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
|
* 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.
|
||||||
@@ -940,6 +1096,22 @@ public final class DataProcessor {
|
|||||||
return true;
|
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) {
|
private static long getTimestampWithDayDiff(final long timestamp, final int dayDiff) {
|
||||||
final Calendar calendar = Calendar.getInstance();
|
final Calendar calendar = Calendar.getInstance();
|
||||||
calendar.setTimeInMillis(timestamp);
|
calendar.setTimeInMillis(timestamp);
|
||||||
@@ -1006,6 +1178,21 @@ public final class DataProcessor {
|
|||||||
return batteryDiffEntry;
|
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,
|
private static void log(Context context, final String content, final long timestamp,
|
||||||
final BatteryHistEntry entry) {
|
final BatteryHistEntry entry) {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
@@ -1015,12 +1202,12 @@ public final class DataProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Compute diff map and loads all items (icon and label) in the background.
|
// 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>>> {
|
extends AsyncTask<Void, Void, Map<Integer, Map<Integer, BatteryDiffData>>> {
|
||||||
|
|
||||||
private Context mApplicationContext;
|
Context mApplicationContext;
|
||||||
private Handler mHandler;
|
final Handler mHandler;
|
||||||
private UsageMapAsyncResponse mAsyncResponseDelegate;
|
final UsageMapAsyncResponse mAsyncResponseDelegate;
|
||||||
private List<BatteryLevelData.PeriodBatteryLevelData> mHourlyBatteryLevelsPerDay;
|
private List<BatteryLevelData.PeriodBatteryLevelData> mHourlyBatteryLevelsPerDay;
|
||||||
private Map<Long, Map<String, BatteryHistEntry>> mBatteryHistoryMap;
|
private Map<Long, Map<String, BatteryHistEntry>> mBatteryHistoryMap;
|
||||||
|
|
||||||
@@ -1051,17 +1238,7 @@ public final class DataProcessor {
|
|||||||
final Map<Integer, Map<Integer, BatteryDiffData>> batteryUsageMap =
|
final Map<Integer, Map<Integer, BatteryDiffData>> batteryUsageMap =
|
||||||
getBatteryUsageMap(
|
getBatteryUsageMap(
|
||||||
mApplicationContext, mHourlyBatteryLevelsPerDay, mBatteryHistoryMap);
|
mApplicationContext, mHourlyBatteryLevelsPerDay, mBatteryHistoryMap);
|
||||||
if (batteryUsageMap != null) {
|
loadLabelAndIcon(batteryUsageMap);
|
||||||
// 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",
|
Log.d(TAG, String.format("execute ComputeUsageMapAndLoadItemsTask in %d/ms",
|
||||||
(System.currentTimeMillis() - startTime)));
|
(System.currentTimeMillis() - startTime)));
|
||||||
return batteryUsageMap;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -64,7 +64,7 @@ public final class BatteryHistEntryTest {
|
|||||||
when(mMockBatteryEntry.getConsumerType())
|
when(mMockBatteryEntry.getConsumerType())
|
||||||
.thenReturn(ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY);
|
.thenReturn(ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY);
|
||||||
final ContentValues values =
|
final ContentValues values =
|
||||||
ConvertUtils.convert(
|
ConvertUtils.convertToContentValues(
|
||||||
mMockBatteryEntry,
|
mMockBatteryEntry,
|
||||||
mBatteryUsageStats,
|
mBatteryUsageStats,
|
||||||
/*batteryLevel=*/ 12,
|
/*batteryLevel=*/ 12,
|
||||||
|
@@ -71,7 +71,7 @@ public final class ConvertUtilsTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void convert_returnsExpectedContentValues() {
|
public void convertToContentValues_returnsExpectedContentValues() {
|
||||||
final int expectedType = 3;
|
final int expectedType = 3;
|
||||||
when(mMockBatteryEntry.getUid()).thenReturn(1001);
|
when(mMockBatteryEntry.getUid()).thenReturn(1001);
|
||||||
when(mMockBatteryEntry.getLabel()).thenReturn("Settings");
|
when(mMockBatteryEntry.getLabel()).thenReturn("Settings");
|
||||||
@@ -88,7 +88,7 @@ public final class ConvertUtilsTest {
|
|||||||
.thenReturn(ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY);
|
.thenReturn(ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY);
|
||||||
|
|
||||||
final ContentValues values =
|
final ContentValues values =
|
||||||
ConvertUtils.convert(
|
ConvertUtils.convertToContentValues(
|
||||||
mMockBatteryEntry,
|
mMockBatteryEntry,
|
||||||
mBatteryUsageStats,
|
mBatteryUsageStats,
|
||||||
/*batteryLevel=*/ 12,
|
/*batteryLevel=*/ 12,
|
||||||
@@ -128,9 +128,9 @@ public final class ConvertUtilsTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void convert_nullBatteryEntry_returnsExpectedContentValues() {
|
public void convertToContentValues_nullBatteryEntry_returnsExpectedContentValues() {
|
||||||
final ContentValues values =
|
final ContentValues values =
|
||||||
ConvertUtils.convert(
|
ConvertUtils.convertToContentValues(
|
||||||
/*entry=*/ null,
|
/*entry=*/ null,
|
||||||
/*batteryUsageStats=*/ null,
|
/*batteryUsageStats=*/ null,
|
||||||
/*batteryLevel=*/ 12,
|
/*batteryLevel=*/ 12,
|
||||||
@@ -154,6 +154,76 @@ public final class ConvertUtilsTest {
|
|||||||
.isEqualTo(ConvertUtils.FAKE_PACKAGE_NAME);
|
.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
|
@Test
|
||||||
public void getIndexedUsageMap_nullOrEmptyHistoryMap_returnEmptyCollection() {
|
public void getIndexedUsageMap_nullOrEmptyHistoryMap_returnEmptyCollection() {
|
||||||
final int timeSlotSize = 2;
|
final int timeSlotSize = 2;
|
||||||
|
@@ -18,6 +18,7 @@ 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.doReturn;
|
||||||
import static org.mockito.Mockito.never;
|
import static org.mockito.Mockito.never;
|
||||||
import static org.mockito.Mockito.spy;
|
import static org.mockito.Mockito.spy;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
@@ -26,6 +27,8 @@ import static org.mockito.Mockito.when;
|
|||||||
import android.app.settings.SettingsEnums;
|
import android.app.settings.SettingsEnums;
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.os.BatteryConsumer;
|
||||||
|
import android.os.BatteryUsageStats;
|
||||||
import android.text.format.DateUtils;
|
import android.text.format.DateUtils;
|
||||||
|
|
||||||
import com.android.settings.fuelgauge.BatteryUtils;
|
import com.android.settings.fuelgauge.BatteryUtils;
|
||||||
@@ -36,6 +39,7 @@ import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
|
|||||||
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;
|
||||||
|
import org.mockito.Mock;
|
||||||
import org.mockito.MockitoAnnotations;
|
import org.mockito.MockitoAnnotations;
|
||||||
import org.robolectric.RobolectricTestRunner;
|
import org.robolectric.RobolectricTestRunner;
|
||||||
import org.robolectric.RuntimeEnvironment;
|
import org.robolectric.RuntimeEnvironment;
|
||||||
@@ -59,6 +63,13 @@ public class DataProcessorTest {
|
|||||||
private MetricsFeatureProvider mMetricsFeatureProvider;
|
private MetricsFeatureProvider mMetricsFeatureProvider;
|
||||||
private PowerUsageFeatureProvider mPowerUsageFeatureProvider;
|
private PowerUsageFeatureProvider mPowerUsageFeatureProvider;
|
||||||
|
|
||||||
|
@Mock private BatteryUsageStats mBatteryUsageStats;
|
||||||
|
@Mock private BatteryEntry mMockBatteryEntry1;
|
||||||
|
@Mock private BatteryEntry mMockBatteryEntry2;
|
||||||
|
@Mock private BatteryEntry mMockBatteryEntry3;
|
||||||
|
@Mock private BatteryEntry mMockBatteryEntry4;
|
||||||
|
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
MockitoAnnotations.initMocks(this);
|
MockitoAnnotations.initMocks(this);
|
||||||
@@ -883,6 +894,60 @@ public class DataProcessorTest {
|
|||||||
.action(mContext, SettingsEnums.ACTION_BATTERY_USAGE_HIDDEN_APP_COUNT, 0);
|
.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(
|
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<>();
|
||||||
|
Reference in New Issue
Block a user