Merge "Load application icon and label for UID battery consumer type" into sc-dev
diff --git a/src/com/android/settings/fuelgauge/BatteryDiffEntry.java b/src/com/android/settings/fuelgauge/BatteryDiffEntry.java
index 1f61b8b..efc5554 100644
--- a/src/com/android/settings/fuelgauge/BatteryDiffEntry.java
+++ b/src/com/android/settings/fuelgauge/BatteryDiffEntry.java
@@ -22,6 +22,8 @@
 import android.graphics.drawable.Drawable;
 import android.util.Log;
 
+import androidx.annotation.VisibleForTesting;
+
 import java.time.Duration;
 import java.util.Comparator;
 
@@ -42,9 +44,11 @@
     private double mPercentOfTotal;
 
     private Context mContext;
-    private String mAppLabel = null;
-    private Drawable mAppIcon = null;
-    private boolean mIsLoaded = false;
+    private String mDefaultPackageName = null;
+
+    @VisibleForTesting String mAppLabel = null;
+    @VisibleForTesting Drawable mAppIcon = null;
+    @VisibleForTesting boolean mIsLoaded = false;
 
     public BatteryDiffEntry(
             Context context,
@@ -84,13 +88,28 @@
     /** Gets the app label name for this entry. */
     public String getAppLabel() {
         loadLabelAndIcon();
-        return mAppLabel;
+        // Returns default applicationn label if we cannot find it.
+        return mAppLabel == null || mAppLabel.length() == 0
+            ? mBatteryHistEntry.mAppLabel
+            : mAppLabel;
     }
 
     /** Gets the app icon {@link Drawable} for this entry. */
     public Drawable getAppIcon() {
         loadLabelAndIcon();
-        return mAppIcon;
+        if (mBatteryHistEntry.mConsumerType !=
+                ConvertUtils.CONSUMER_TYPE_UID_BATTERY) {
+            return mAppIcon;
+        }
+        // Returns default application icon if UID_BATTERY icon is null.
+        return mAppIcon == null
+            ? mContext.getPackageManager().getDefaultActivityIcon()
+            : mAppIcon;
+    }
+
+    /** Gets the searching package name for UID battery type. */
+    public String getPackageName() {
+        return mDefaultPackageName;
     }
 
     private void loadLabelAndIcon() {
@@ -127,7 +146,46 @@
     }
 
     private void loadNameAndIconForUid() {
-        // TODO(b/185187669) fetch label and icon for UID battery type
+        final String packageName = mBatteryHistEntry.mPackageName;
+        final PackageManager packageManager = mContext.getPackageManager();
+        // Gets the application label from PackageManager.
+        if (packageName != null && packageName.length() != 0) {
+            try {
+                final ApplicationInfo appInfo =
+                    packageManager.getApplicationInfo(packageName, /*no flags*/ 0);
+                mAppLabel = packageManager.getApplicationLabel(appInfo).toString();
+            } catch (NameNotFoundException e) {
+                Log.e(TAG, "failed to retrieve ApplicationInfo for: " + packageName);
+                mAppLabel = packageName;
+            }
+        }
+
+        final int uid = (int) mBatteryHistEntry.mUid;
+        final String[] packages = packageManager.getPackagesForUid(uid);
+        // Loads special defined application label and icon if available.
+        if (packages == null || packages.length == 0) {
+            final BatteryEntry.NameAndIcon nameAndIcon =
+                BatteryEntry.getNameAndIconFromUid(mContext, mAppLabel, uid);
+            mAppLabel = nameAndIcon.name;
+            mAppIcon = nameAndIcon.icon;
+        }
+
+        final BatteryEntry.NameAndIcon nameAndIcon =
+            BatteryEntry.loadNameAndIcon(
+                mContext, uid, /*uid=*/ null, /*batteryEntry=*/ null,
+                packageName, mAppLabel, mAppIcon);
+        // Clears BatteryEntry internal cache since we will have another one.
+        BatteryEntry.clearUidCache();
+        if (nameAndIcon != null) {
+            mAppLabel = getNonNull(mAppLabel, nameAndIcon.name);
+            mAppIcon = getNonNull(mAppIcon, nameAndIcon.icon);
+            mDefaultPackageName = nameAndIcon.packageName;
+            if (mDefaultPackageName != null
+                    && !mDefaultPackageName.equals(nameAndIcon.packageName)) {
+                Log.w(TAG, String.format("found different package: %s | %s",
+                    mDefaultPackageName, nameAndIcon.packageName));
+            }
+        }
     }
 
     @Override
@@ -144,4 +202,8 @@
                   mBatteryHistEntry.mPackageName, mBatteryHistEntry.mUid));
         return builder.toString();
     }
+
+    private static <T> T getNonNull(T originalObj, T newObj) {
+        return newObj != null ? newObj : originalObj;
+    }
 }
diff --git a/src/com/android/settings/fuelgauge/BatteryEntry.java b/src/com/android/settings/fuelgauge/BatteryEntry.java
index c0ed092..2d9f715 100644
--- a/src/com/android/settings/fuelgauge/BatteryEntry.java
+++ b/src/com/android/settings/fuelgauge/BatteryEntry.java
@@ -110,7 +110,8 @@
                 }
                 final NameAndIcon nameAndIcon =
                     BatteryEntry.loadNameAndIcon(
-                        be.mContext, be.getUid(), sHandler, be, be.mDefaultPackageName);
+                        be.mContext, be.getUid(), sHandler, be,
+                        be.mDefaultPackageName, be.name, be.icon);
                 if (nameAndIcon != null) {
                     be.icon = getNonNull(be.icon, nameAndIcon.icon);
                     be.name = getNonNull(be.name, nameAndIcon.name);
@@ -273,6 +274,7 @@
             icon = mContext.getPackageManager().getDefaultActivityIcon();
         }
 
+        // Avoids post the loading icon and label in the background request.
         if (sHandler != null && loadDataInBackground) {
             synchronized (sRequestQueue) {
                 sRequestQueue.add(this);
@@ -288,9 +290,9 @@
             int uid,
             Handler handler,
             BatteryEntry batteryEntry,
-            String defaultPackageName) {
-        String name = null;
-        Drawable icon = null;
+            String defaultPackageName,
+            String name,
+            Drawable icon) {
         // Bail out if the current sipper is not an App sipper.
         if (uid == 0 || uid == Process.INVALID_UID) {
             return null;
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryDiffEntryTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryDiffEntryTest.java
index afbfe84..0f7a407 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryDiffEntryTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryDiffEntryTest.java
@@ -22,6 +22,9 @@
 
 import android.content.Context;
 import android.content.ContentValues;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
 import android.os.SystemBatteryConsumer;
 import android.os.UserManager;
 
@@ -41,14 +44,18 @@
 public final class BatteryDiffEntryTest {
 
     private Context mContext;
-    @Mock
-    private UserManager mockUserManager;
+
+    @Mock private ApplicationInfo mockAppInfo;
+    @Mock private PackageManager mockPackageManager;
+    @Mock private UserManager mockUserManager;
+    @Mock private Drawable mockDrawable;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mContext = spy(RuntimeEnvironment.application);
         doReturn(mockUserManager).when(mContext).getSystemService(UserManager.class);
+        doReturn(mockPackageManager).when(mContext).getPackageManager();
     }
 
     @Test
@@ -96,9 +103,8 @@
     @Test
     public void testLoadLabelAndIcon_forSystemBattery_returnExpectedResult() {
         // Generates fake testing data.
-        final ContentValues values = new ContentValues();
-        values.put("consumerType",
-            Integer.valueOf(ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY));
+        final ContentValues values = getContentValuesWithType(
+            ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY);
         values.put("drainType",
             Integer.valueOf(SystemBatteryConsumer.DRAIN_TYPE_AMBIENT_DISPLAY));
         final BatteryHistEntry batteryHistEntry = new BatteryHistEntry(values);
@@ -112,9 +118,8 @@
     public void testLoadLabelAndIcon_forUserBattery_returnExpectedResult() {
         doReturn(null).when(mockUserManager).getUserInfo(1001);
         // Generates fake testing data.
-        final ContentValues values = new ContentValues();
-        values.put("consumerType",
-            Integer.valueOf(ConvertUtils.CONSUMER_TYPE_USER_BATTERY));
+        final ContentValues values = getContentValuesWithType(
+            ConvertUtils.CONSUMER_TYPE_USER_BATTERY);
         values.put("userId", Integer.valueOf(1001));
         final BatteryHistEntry batteryHistEntry = new BatteryHistEntry(values);
 
@@ -124,6 +129,77 @@
         assertThat(entry.getAppIcon()).isNull();
     }
 
+    @Test
+    public void testGetAppLabel_loadDataFromApplicationInfo() throws Exception {
+        final String expectedAppLabel = "fake app label";
+        final String fakePackageName = "com.fake.google.com";
+        final ContentValues values = getContentValuesWithType(
+            ConvertUtils.CONSUMER_TYPE_UID_BATTERY);
+        values.put("uid", /*invalid uid*/ 10001);
+        values.put("packageName", fakePackageName);
+        doReturn(mockAppInfo).when(mockPackageManager)
+            .getApplicationInfo(fakePackageName, 0);
+        doReturn(expectedAppLabel).when(mockPackageManager)
+            .getApplicationLabel(mockAppInfo);
+        final BatteryHistEntry batteryHistEntry = new BatteryHistEntry(values);
+
+        final BatteryDiffEntry entry = createBatteryDiffEntry(10, batteryHistEntry);
+
+        assertThat(entry.getAppLabel()).isEqualTo(expectedAppLabel);
+    }
+
+    @Test
+    public void testGetAppLabel_loadDataFromPreDefinedNameAndUid() {
+        final ContentValues values = getContentValuesWithType(
+            ConvertUtils.CONSUMER_TYPE_UID_BATTERY);
+        final BatteryHistEntry batteryHistEntry = new BatteryHistEntry(values);
+
+        final BatteryDiffEntry entry = createBatteryDiffEntry(10, batteryHistEntry);
+
+        assertThat(entry.getAppLabel()).isEqualTo("Android OS");
+    }
+
+    @Test
+    public void testGetAppLabel_nullAppLabel_returnAppLabelInBatteryHistEntry() {
+        final String expectedAppLabel = "fake app label";
+        final ContentValues values = getContentValuesWithType(
+            ConvertUtils.CONSUMER_TYPE_UID_BATTERY);
+        values.put("appLabel", expectedAppLabel);
+        final BatteryHistEntry batteryHistEntry = new BatteryHistEntry(values);
+
+        final BatteryDiffEntry entry = createBatteryDiffEntry(10, batteryHistEntry);
+
+        entry.mIsLoaded = true;
+        assertThat(entry.getAppLabel()).isEqualTo(expectedAppLabel);
+    }
+
+    @Test
+    public void testGetAppIcon_nonUidConsumer_returnAppIconInBatteryDiffEntry() {
+        final ContentValues values = getContentValuesWithType(
+            ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY);
+        final BatteryHistEntry batteryHistEntry = new BatteryHistEntry(values);
+
+        final BatteryDiffEntry entry = createBatteryDiffEntry(10, batteryHistEntry);
+
+        entry.mIsLoaded = true;
+        entry.mAppIcon = mockDrawable;
+        assertThat(entry.getAppIcon()).isEqualTo(mockDrawable);
+    }
+
+    @Test
+    public void testGetAppIcon_uidConsumerWithNullIcon_returnDefaultActivityIcon() {
+        final ContentValues values = getContentValuesWithType(
+            ConvertUtils.CONSUMER_TYPE_UID_BATTERY);
+        final BatteryHistEntry batteryHistEntry = new BatteryHistEntry(values);
+        doReturn(mockDrawable).when(mockPackageManager).getDefaultActivityIcon();
+
+        final BatteryDiffEntry entry = createBatteryDiffEntry(10, batteryHistEntry);
+
+        entry.mIsLoaded = true;
+        entry.mAppIcon = null;
+        assertThat(entry.getAppIcon()).isEqualTo(mockDrawable);
+    }
+
     private BatteryDiffEntry createBatteryDiffEntry(
         double consumePower, BatteryHistEntry batteryHistEntry) {
         final BatteryDiffEntry entry = new BatteryDiffEntry(
@@ -135,4 +211,10 @@
         entry.setTotalConsumePower(100.0);
         return entry;
     }
+
+    private static ContentValues getContentValuesWithType(int consumerType) {
+        final ContentValues values = new ContentValues();
+        values.put("consumerType", Integer.valueOf(consumerType));
+        return values;
+    }
 }