From bd9042c47ff66af4d159764595b226a8352b7eda Mon Sep 17 00:00:00 2001 From: Kuan Wang Date: Wed, 14 Sep 2022 21:51:17 +0800 Subject: [PATCH] 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 --- .../BatteryChartPreferenceController.java | 90 ++++-- .../fuelgauge/batteryusage/ConvertUtils.java | 22 +- .../fuelgauge/batteryusage/DataProcessor.java | 276 +++++++++++++++--- .../batteryusage/BatteryHistEntryTest.java | 2 +- .../batteryusage/ConvertUtilsTest.java | 78 ++++- .../batteryusage/DataProcessorTest.java | 65 +++++ 6 files changed, 468 insertions(+), 65 deletions(-) diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java index 79f0880c079..69b19d3a873 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java +++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java @@ -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 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 diff --git a/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java b/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java index 68f0dc78aa7..130357378f1 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java +++ b/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java @@ -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); diff --git a/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java b/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java index f493eceb206..b9c3a6ff7f3 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java +++ b/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java @@ -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> 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 batteryEntryList, + final BatteryUsageStats batteryUsageStats) { + final List 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 appEntries = new ArrayList<>(); + final List 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> getBatteryUsageMapFromStatsService( + final Context context) { + final Map> resultMap = new HashMap<>(); + final Map 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 batteryEntryList = + generateBatteryEntryListFromBatteryUsageStats(context, batteryUsageStats); + batteryDiffData = generateBatteryDiffData(context, batteryEntryList, batteryUsageStats); + } catch (RuntimeException e) { + Log.e(TAG, "load batteryUsageStats:" + e); + } + + return batteryDiffData; + } + + @Nullable + private static List 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 convertToBatteryHistEntry( + @Nullable final List 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> 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>> { - private Context mApplicationContext; - private Handler mHandler; - private UsageMapAsyncResponse mAsyncResponseDelegate; + Context mApplicationContext; + final Handler mHandler; + final UsageMapAsyncResponse mAsyncResponseDelegate; private List mHourlyBatteryLevelsPerDay; private Map> mBatteryHistoryMap; @@ -1051,17 +1238,7 @@ public final class DataProcessor { final Map> 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> 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> batteryUsageMap = + getBatteryUsageMapFromStatsService(mApplicationContext); + loadLabelAndIcon(batteryUsageMap); + Log.d(TAG, String.format("execute LoadUsageMapFromBatteryStatsServiceTask in %d/ms", + (System.currentTimeMillis() - startTime))); + return batteryUsageMap; + } + } } diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryHistEntryTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryHistEntryTest.java index 553c0895c0d..5dfdd39246b 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryHistEntryTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryHistEntryTest.java @@ -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, diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ConvertUtilsTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ConvertUtilsTest.java index c9bac030ac8..2ae73b103e5 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ConvertUtilsTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ConvertUtilsTest.java @@ -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; diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessorTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessorTest.java index 84f9310a28a..c4832041314 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessorTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessorTest.java @@ -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 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> createHistoryMap( final long[] timestamps, final int[] levels) { final Map> batteryHistoryMap = new HashMap<>();