Merge "Begin transition from BatteryStatsHelper to BatteryUsageStats API." into sc-dev
diff --git a/src/com/android/settings/applications/appinfo/AppBatteryPreferenceController.java b/src/com/android/settings/applications/appinfo/AppBatteryPreferenceController.java
index 6e4818a..83b4241 100644
--- a/src/com/android/settings/applications/appinfo/AppBatteryPreferenceController.java
+++ b/src/com/android/settings/applications/appinfo/AppBatteryPreferenceController.java
@@ -19,9 +19,12 @@
 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.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.List;
 
 public class AppBatteryPreferenceController extends BasePreferenceController
-        implements LoaderManager.LoaderCallbacks<BatteryStatsHelper>,
-        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 @@
     @VisibleForTesting
     BatteryUtils mBatteryUtils;
 
+    @VisibleForTesting
+    BatteryUsageStats mBatteryUsageStats;
+    @VisibleForTesting
+    UidBatteryConsumer mUidBatteryConsumer;
+
     private Preference mPreference;
     private final AppInfoDashboardFragment mParent;
     private String mBatteryPercent;
@@ -96,7 +113,8 @@
         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 @@
     @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<BatteryStatsHelper> 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<BatteryStatsHelper> 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<BatteryStatsHelper> 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<BatterySipper> 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 @@
 
     @VisibleForTesting
     boolean isBatteryStatsAvailable() {
-        return mBatteryHelper != null && mSipper != null;
+        return mBatteryHelper != null && mSipper != null && mUidBatteryConsumer != null;
     }
 
     @VisibleForTesting
@@ -176,4 +194,54 @@
         return null;
     }
 
+    @VisibleForTesting
+    UidBatteryConsumer findTargetUidBatteryConsumer(BatteryUsageStats batteryUsageStats, int uid) {
+        final List<UidBatteryConsumer> 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<BatteryStatsHelper> {
+        @Override
+        public Loader<BatteryStatsHelper> onCreateLoader(int id, Bundle args) {
+            return new BatteryStatsHelperLoader(mContext);
+        }
+
+        @Override
+        public void onLoadFinished(Loader<BatteryStatsHelper> loader,
+                BatteryStatsHelper batteryHelper) {
+            mBatteryHelper = batteryHelper;
+            AppBatteryPreferenceController.this.onLoadFinished();
+        }
+
+        @Override
+        public void onLoaderReset(Loader<BatteryStatsHelper> loader) {
+        }
+    }
+
+    private class BatteryUsageStatsLoaderCallbacks
+            implements LoaderManager.LoaderCallbacks<BatteryUsageStats> {
+        @Override
+        @NonNull
+        public Loader<BatteryUsageStats> onCreateLoader(int id, Bundle args) {
+            return new BatteryUsageStatsLoader(mContext);
+        }
+
+        @Override
+        public void onLoadFinished(Loader<BatteryUsageStats> loader,
+                BatteryUsageStats batteryUsageStats) {
+            mBatteryUsageStats = batteryUsageStats;
+            AppBatteryPreferenceController.this.onLoadFinished();
+        }
+
+        @Override
+        public void onLoaderReset(Loader<BatteryUsageStats> loader) {
+        }
+    }
 }
diff --git a/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java b/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java
index f584408..6a86c71c 100755
--- a/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java
+++ b/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java
@@ -95,6 +95,7 @@
     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 8d7bcd9..1a9db03 100644
--- a/src/com/android/settings/fuelgauge/BatteryAppListPreferenceController.java
+++ b/src/com/android/settings/fuelgauge/BatteryAppListPreferenceController.java
@@ -208,7 +208,7 @@
                 }
                 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 38ae2b2..d533c80 100644
--- a/src/com/android/settings/fuelgauge/BatteryEntry.java
+++ b/src/com/android/settings/fuelgauge/BatteryEntry.java
@@ -28,6 +28,7 @@
 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 final Context context;
     public final BatterySipper sipper;
+    public final UidBatteryConsumer uidBatteryConsumer;
 
     public String name;
     public Drawable icon;
@@ -134,10 +136,41 @@
         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 0000000..5b184a9
--- /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<BatteryUsageStats> {
+    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 154856e..c735452 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.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 @@
     @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 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 void isBatteryStatsAvailable_hasBatteryStatsHelperAndSipper_returnTrue() {
         mController.mBatteryHelper = mBatteryStatsHelper;
         mController.mSipper = mBatterySipper;
+        mController.mBatteryUsageStats = mBatteryUsageStats;
+        mController.mUidBatteryConsumer = mUidBatteryConsumer;
 
         assertThat(mController.isBatteryStatsAvailable()).isTrue();
     }
@@ -175,6 +185,8 @@
         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 @@
         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 fceee7e..e40b270 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 @@
     }
 
     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 @@
     }
 
     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 @@
         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");