From 97b3325ece9009fb390a4afb293983dd8f12b10e Mon Sep 17 00:00:00 2001 From: Dmitri Plotnikov Date: Mon, 16 Nov 2020 15:22:21 -0800 Subject: [PATCH] Begin transition from BatteryStatsHelper to BatteryUsageStats API. For now, keep both BatteryStatsHelper and BatteryUsageStats in play. The plan is to transition from the former to the latter, one usage at a time. When all is said and done, all references to BatteryStatsHelper will be gone. Bug: 173745486 Test: atest --host SettingsRoboTests Change-Id: I37e1dfff0043b1845992f18d72067bb547bb69ff --- .../AppBatteryPreferenceController.java | 114 ++++++++++++++---- .../appinfo/AppInfoDashboardFragment.java | 1 + .../BatteryAppListPreferenceController.java | 2 +- .../settings/fuelgauge/BatteryEntry.java | 35 +++++- .../fuelgauge/BatteryUsageStatsLoader.java | 44 +++++++ .../AppBatteryPreferenceControllerTest.java | 15 ++- .../settings/fuelgauge/BatteryEntryTest.java | 8 +- 7 files changed, 190 insertions(+), 29 deletions(-) create mode 100644 src/com/android/settings/fuelgauge/BatteryUsageStatsLoader.java diff --git a/src/com/android/settings/applications/appinfo/AppBatteryPreferenceController.java b/src/com/android/settings/applications/appinfo/AppBatteryPreferenceController.java index 6e4818ab92f..83b42410fad 100644 --- a/src/com/android/settings/applications/appinfo/AppBatteryPreferenceController.java +++ b/src/com/android/settings/applications/appinfo/AppBatteryPreferenceController.java @@ -19,9 +19,12 @@ package com.android.settings.applications.appinfo; import android.content.Context; import android.content.pm.PackageInfo; import android.os.BatteryStats; +import android.os.BatteryUsageStats; import android.os.Bundle; +import android.os.UidBatteryConsumer; import android.os.UserManager; +import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; import androidx.loader.app.LoaderManager; import androidx.loader.content.Loader; @@ -36,6 +39,7 @@ import com.android.settings.core.BasePreferenceController; import com.android.settings.fuelgauge.AdvancedPowerUsageDetail; import com.android.settings.fuelgauge.BatteryEntry; import com.android.settings.fuelgauge.BatteryStatsHelperLoader; +import com.android.settings.fuelgauge.BatteryUsageStatsLoader; import com.android.settings.fuelgauge.BatteryUtils; import com.android.settingslib.core.lifecycle.Lifecycle; import com.android.settingslib.core.lifecycle.LifecycleObserver; @@ -46,11 +50,19 @@ import java.util.ArrayList; import java.util.List; public class AppBatteryPreferenceController extends BasePreferenceController - implements LoaderManager.LoaderCallbacks, - LifecycleObserver, OnResume, OnPause { + implements LifecycleObserver, OnResume, OnPause { private static final String KEY_BATTERY = "battery"; + // TODO(b/180630447): switch to BatteryUsageStatsLoader and remove all references to + // BatteryStatsHelper and BatterySipper + @VisibleForTesting + final BatteryStatsHelperLoaderCallbacks mBatteryStatsHelperLoaderCallbacks = + new BatteryStatsHelperLoaderCallbacks(); + @VisibleForTesting + final BatteryUsageStatsLoaderCallbacks mBatteryUsageStatsLoaderCallbacks = + new BatteryUsageStatsLoaderCallbacks(); + @VisibleForTesting BatterySipper mSipper; @VisibleForTesting @@ -58,6 +70,11 @@ public class AppBatteryPreferenceController extends BasePreferenceController @VisibleForTesting BatteryUtils mBatteryUtils; + @VisibleForTesting + BatteryUsageStats mBatteryUsageStats; + @VisibleForTesting + UidBatteryConsumer mUidBatteryConsumer; + private Preference mPreference; private final AppInfoDashboardFragment mParent; private String mBatteryPercent; @@ -96,7 +113,8 @@ public class AppBatteryPreferenceController extends BasePreferenceController if (isBatteryStatsAvailable()) { final UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); - final BatteryEntry entry = new BatteryEntry(mContext, null, userManager, mSipper); + final BatteryEntry entry = new BatteryEntry(mContext, null, userManager, mSipper, + mUidBatteryConsumer); entry.defaultPackageName = mPackageName; AdvancedPowerUsageDetail.startBatteryDetailPage(mParent.getActivity(), mParent, mBatteryHelper, BatteryStats.STATS_SINCE_CHARGED, entry, mBatteryPercent); @@ -110,48 +128,48 @@ public class AppBatteryPreferenceController extends BasePreferenceController @Override public void onResume() { mParent.getLoaderManager().restartLoader( - mParent.LOADER_BATTERY, Bundle.EMPTY, this); + AppInfoDashboardFragment.LOADER_BATTERY, Bundle.EMPTY, + mBatteryStatsHelperLoaderCallbacks); + mParent.getLoaderManager().restartLoader( + AppInfoDashboardFragment.LOADER_BATTERY_USAGE_STATS, Bundle.EMPTY, + mBatteryUsageStatsLoaderCallbacks); } @Override public void onPause() { - mParent.getLoaderManager().destroyLoader(mParent.LOADER_BATTERY); + mParent.getLoaderManager().destroyLoader(AppInfoDashboardFragment.LOADER_BATTERY); + mParent.getLoaderManager().destroyLoader( + AppInfoDashboardFragment.LOADER_BATTERY_USAGE_STATS); } - @Override - public Loader onCreateLoader(int id, Bundle args) { - return new BatteryStatsHelperLoader(mContext); - } + private void onLoadFinished() { + // Wait for both loaders to finish before proceeding. + if (mBatteryHelper == null || mBatteryUsageStats == null) { + return; + } - @Override - public void onLoadFinished(Loader loader, - BatteryStatsHelper batteryHelper) { - mBatteryHelper = batteryHelper; final PackageInfo packageInfo = mParent.getPackageInfo(); if (packageInfo != null) { - mSipper = findTargetSipper(batteryHelper, packageInfo.applicationInfo.uid); + mSipper = findTargetSipper(mBatteryHelper, packageInfo.applicationInfo.uid); + mUidBatteryConsumer = findTargetUidBatteryConsumer(mBatteryUsageStats, + packageInfo.applicationInfo.uid); if (mParent.getActivity() != null) { updateBattery(); } } } - @Override - public void onLoaderReset(Loader loader) { - } - @VisibleForTesting void updateBattery() { mPreference.setEnabled(true); if (isBatteryStatsAvailable()) { - final int dischargeAmount = mBatteryHelper.getStats().getDischargeAmount( - BatteryStats.STATS_SINCE_CHARGED); + final int dischargePercentage = mBatteryUsageStats.getDischargePercentage(); final List usageList = new ArrayList<>(mBatteryHelper.getUsageList()); final double hiddenAmount = mBatteryUtils.removeHiddenBatterySippers(usageList); final int percentOfMax = (int) mBatteryUtils.calculateBatteryPercent( - mSipper.totalPowerMah, mBatteryHelper.getTotalPower(), hiddenAmount, - dischargeAmount); + mUidBatteryConsumer.getConsumedPower(), mBatteryUsageStats.getConsumedPower(), + hiddenAmount, dischargePercentage); mBatteryPercent = Utils.formatPercentage(percentOfMax); mPreference.setSummary(mContext.getString(R.string.battery_summary, mBatteryPercent)); } else { @@ -161,7 +179,7 @@ public class AppBatteryPreferenceController extends BasePreferenceController @VisibleForTesting boolean isBatteryStatsAvailable() { - return mBatteryHelper != null && mSipper != null; + return mBatteryHelper != null && mSipper != null && mUidBatteryConsumer != null; } @VisibleForTesting @@ -176,4 +194,54 @@ public class AppBatteryPreferenceController extends BasePreferenceController return null; } + @VisibleForTesting + UidBatteryConsumer findTargetUidBatteryConsumer(BatteryUsageStats batteryUsageStats, int uid) { + final List usageList = batteryUsageStats.getUidBatteryConsumers(); + for (int i = 0, size = usageList.size(); i < size; i++) { + final UidBatteryConsumer consumer = usageList.get(i); + if (consumer.getUid() == uid) { + return consumer; + } + } + return null; + } + + private class BatteryStatsHelperLoaderCallbacks + implements LoaderManager.LoaderCallbacks { + @Override + public Loader onCreateLoader(int id, Bundle args) { + return new BatteryStatsHelperLoader(mContext); + } + + @Override + public void onLoadFinished(Loader loader, + BatteryStatsHelper batteryHelper) { + mBatteryHelper = batteryHelper; + AppBatteryPreferenceController.this.onLoadFinished(); + } + + @Override + public void onLoaderReset(Loader loader) { + } + } + + private class BatteryUsageStatsLoaderCallbacks + implements LoaderManager.LoaderCallbacks { + @Override + @NonNull + public Loader onCreateLoader(int id, Bundle args) { + return new BatteryUsageStatsLoader(mContext); + } + + @Override + public void onLoadFinished(Loader loader, + BatteryUsageStats batteryUsageStats) { + mBatteryUsageStats = batteryUsageStats; + AppBatteryPreferenceController.this.onLoadFinished(); + } + + @Override + public void onLoaderReset(Loader loader) { + } + } } diff --git a/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java b/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java index f584408ccdc..6a86c71c1d5 100755 --- a/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java +++ b/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java @@ -95,6 +95,7 @@ public class AppInfoDashboardFragment extends DashboardFragment static final int LOADER_CHART_DATA = 2; static final int LOADER_STORAGE = 3; static final int LOADER_BATTERY = 4; + static final int LOADER_BATTERY_USAGE_STATS = 5; public static final String ARG_PACKAGE_NAME = "package"; public static final String ARG_PACKAGE_UID = "uid"; diff --git a/src/com/android/settings/fuelgauge/BatteryAppListPreferenceController.java b/src/com/android/settings/fuelgauge/BatteryAppListPreferenceController.java index 8d7bcd96ea6..1a9db0352b8 100644 --- a/src/com/android/settings/fuelgauge/BatteryAppListPreferenceController.java +++ b/src/com/android/settings/fuelgauge/BatteryAppListPreferenceController.java @@ -208,7 +208,7 @@ public class BatteryAppListPreferenceController extends AbstractPreferenceContro } final UserHandle userHandle = new UserHandle(UserHandle.getUserId(sipper.getUid())); final BatteryEntry entry = new BatteryEntry(mActivity, mHandler, mUserManager, - sipper); + sipper, null); final Drawable badgedIcon = mUserManager.getBadgedIconForUser(entry.getIcon(), userHandle); final CharSequence contentDescription = mUserManager.getBadgedLabelForUser( diff --git a/src/com/android/settings/fuelgauge/BatteryEntry.java b/src/com/android/settings/fuelgauge/BatteryEntry.java index 38ae2b2df76..d533c80ceec 100644 --- a/src/com/android/settings/fuelgauge/BatteryEntry.java +++ b/src/com/android/settings/fuelgauge/BatteryEntry.java @@ -28,6 +28,7 @@ import android.graphics.drawable.Drawable; import android.os.Handler; import android.os.Process; import android.os.RemoteException; +import android.os.UidBatteryConsumer; import android.os.UserHandle; import android.os.UserManager; import android.util.Log; @@ -122,6 +123,7 @@ public class BatteryEntry { public final Context context; public final BatterySipper sipper; + public final UidBatteryConsumer uidBatteryConsumer; public String name; public Drawable icon; @@ -134,10 +136,41 @@ public class BatteryEntry { Drawable icon; } - public BatteryEntry(Context context, Handler handler, UserManager um, BatterySipper sipper) { + public BatteryEntry(Context context, Handler handler, UserManager um, BatterySipper sipper, + UidBatteryConsumer uidBatteryConsumer) { sHandler = handler; this.context = context; this.sipper = sipper; + this.uidBatteryConsumer = uidBatteryConsumer; + + // This condition is met when BatteryEntry is initialized from BatteryUsageStats. + // Once the conversion from BatteryStatsHelper is completed, the condition will + // always be true and can be removed. + if (uidBatteryConsumer != null) { + PackageManager pm = context.getPackageManager(); + int uid = uidBatteryConsumer.getUid(); + String[] packages = pm.getPackagesForUid(uid); + // Apps should only have one package + if (packages == null || packages.length != 1) { + name = uidBatteryConsumer.getPackageWithHighestDrain(); + } else { + defaultPackageName = packages[0]; + try { + ApplicationInfo appInfo = + pm.getApplicationInfo(defaultPackageName, 0 /* no flags */); + name = pm.getApplicationLabel(appInfo).toString(); + } catch (NameNotFoundException e) { + Log.d(TAG, "PackageManager failed to retrieve ApplicationInfo for: " + + defaultPackageName); + name = defaultPackageName; + } + } + if ((name == null || iconId == 0) && uid != 0) { + getQuickNameIconForUid(uid); + } + return; + } + switch (sipper.drainType) { case IDLE: name = context.getResources().getString(R.string.power_idle); diff --git a/src/com/android/settings/fuelgauge/BatteryUsageStatsLoader.java b/src/com/android/settings/fuelgauge/BatteryUsageStatsLoader.java new file mode 100644 index 00000000000..5b184a9cd52 --- /dev/null +++ b/src/com/android/settings/fuelgauge/BatteryUsageStatsLoader.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.fuelgauge; + +import android.content.Context; +import android.os.BatteryStatsManager; +import android.os.BatteryUsageStats; + +import com.android.settingslib.utils.AsyncLoaderCompat; + +/** + * Loader to get new {@link BatteryUsageStats} in the background + */ +public class BatteryUsageStatsLoader extends AsyncLoaderCompat { + private final BatteryStatsManager mBatteryStatsManager; + + public BatteryUsageStatsLoader(Context context) { + super(context); + mBatteryStatsManager = context.getSystemService(BatteryStatsManager.class); + } + + @Override + public BatteryUsageStats loadInBackground() { + return mBatteryStatsManager.getBatteryUsageStats(); + } + + @Override + protected void onDiscardResult(BatteryUsageStats result) { + } +} diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/AppBatteryPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/AppBatteryPreferenceControllerTest.java index 154856ebeb9..c735452732a 100644 --- a/tests/robotests/src/com/android/settings/applications/appinfo/AppBatteryPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/applications/appinfo/AppBatteryPreferenceControllerTest.java @@ -31,7 +31,9 @@ import android.content.Context; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.os.BatteryStats; +import android.os.BatteryUsageStats; import android.os.Bundle; +import android.os.UidBatteryConsumer; import androidx.loader.app.LoaderManager; import androidx.preference.Preference; @@ -67,6 +69,10 @@ public class AppBatteryPreferenceControllerTest { @Mock private BatteryUtils mBatteryUtils; @Mock + private BatteryUsageStats mBatteryUsageStats; + @Mock + private UidBatteryConsumer mUidBatteryConsumer; + @Mock private BatterySipper mBatterySipper; @Mock private BatterySipper mOtherBatterySipper; @@ -143,6 +149,8 @@ public class AppBatteryPreferenceControllerTest { public void updateBattery_hasBatteryStats_summaryPercent() { mController.mBatteryHelper = mBatteryStatsHelper; mController.mSipper = mBatterySipper; + mController.mBatteryUsageStats = mBatteryUsageStats; + mController.mUidBatteryConsumer = mUidBatteryConsumer; doReturn(BATTERY_LEVEL).when(mBatteryUtils).calculateBatteryPercent(anyDouble(), anyDouble(), anyDouble(), anyInt()); doReturn(new ArrayList<>()).when(mBatteryStatsHelper).getUsageList(); @@ -157,6 +165,8 @@ public class AppBatteryPreferenceControllerTest { public void isBatteryStatsAvailable_hasBatteryStatsHelperAndSipper_returnTrue() { mController.mBatteryHelper = mBatteryStatsHelper; mController.mSipper = mBatterySipper; + mController.mBatteryUsageStats = mBatteryUsageStats; + mController.mUidBatteryConsumer = mUidBatteryConsumer; assertThat(mController.isBatteryStatsAvailable()).isTrue(); } @@ -175,6 +185,8 @@ public class AppBatteryPreferenceControllerTest { when(mBatteryPreference.getKey()).thenReturn(key); mController.mSipper = mBatterySipper; mController.mBatteryHelper = mBatteryStatsHelper; + mController.mBatteryUsageStats = mBatteryUsageStats; + mController.mUidBatteryConsumer = mUidBatteryConsumer; // Should not crash mController.handlePreferenceTreeClick(mBatteryPreference); @@ -187,7 +199,8 @@ public class AppBatteryPreferenceControllerTest { mController.onResume(); verify(mLoaderManager) - .restartLoader(AppInfoDashboardFragment.LOADER_BATTERY, Bundle.EMPTY, mController); + .restartLoader(AppInfoDashboardFragment.LOADER_BATTERY, Bundle.EMPTY, + mController.mBatteryStatsHelperLoaderCallbacks); } @Test diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryEntryTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryEntryTest.java index fceee7ef67a..e40b27067cc 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryEntryTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryEntryTest.java @@ -79,7 +79,8 @@ public class BatteryEntryTest { } private BatteryEntry createBatteryEntryForApp() { - return new BatteryEntry(mockContext, mockHandler, mockUserManager, createSipperForApp()); + return new BatteryEntry(mockContext, mockHandler, mockUserManager, createSipperForApp(), + null); } private BatterySipper createSipperForApp() { @@ -90,7 +91,8 @@ public class BatteryEntryTest { } private BatteryEntry createBatteryEntryForSystem() { - return new BatteryEntry(mockContext, mockHandler, mockUserManager, createSipperForSystem()); + return new BatteryEntry(mockContext, mockHandler, mockUserManager, createSipperForSystem(), + null); } private BatterySipper createSipperForSystem() { @@ -144,7 +146,7 @@ public class BatteryEntryTest { final BatterySipper batterySipper = mock(BatterySipper.class); batterySipper.drainType = DrainType.AMBIENT_DISPLAY; final BatteryEntry entry = new BatteryEntry(RuntimeEnvironment.application, mockHandler, - mockUserManager, batterySipper); + mockUserManager, batterySipper, null); assertThat(entry.iconId).isEqualTo(R.drawable.ic_settings_aod); assertThat(entry.name).isEqualTo("Ambient display");