diff --git a/src/com/android/settings/fuelgauge/BatteryEntry.java b/src/com/android/settings/fuelgauge/BatteryEntry.java index b0e6969389a..aa7125282f5 100644 --- a/src/com/android/settings/fuelgauge/BatteryEntry.java +++ b/src/com/android/settings/fuelgauge/BatteryEntry.java @@ -22,6 +22,7 @@ import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.UserInfo; import android.graphics.drawable.Drawable; import android.os.Handler; @@ -45,6 +46,8 @@ public class BatteryEntry { public static final int MSG_UPDATE_NAME_ICON = 1; public static final int MSG_REPORT_FULLY_DRAWN = 2; + private static final String TAG = "BatteryEntry"; + static final HashMap sUidCache = new HashMap(); static final ArrayList mRequestQueue = new ArrayList(); @@ -160,7 +163,23 @@ public class BatteryEntry { iconId = R.drawable.ic_settings_display; break; case APP: - name = sipper.packageWithHighestDrain; + PackageManager pm = context.getPackageManager(); + sipper.mPackages = pm.getPackagesForUid(sipper.uidObj.getUid()); + // Apps should only have one package + if (sipper.mPackages == null || sipper.mPackages.length != 1) { + name = sipper.packageWithHighestDrain; + } else { + defaultPackageName = pm.getPackagesForUid(sipper.uidObj.getUid())[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; + } + } break; case USER: { UserInfo info = um.getUserInfo(sipper.userId); @@ -246,7 +265,9 @@ public class BatteryEntry { PackageManager pm = context.getPackageManager(); final int uid = sipper.uidObj.getUid(); - sipper.mPackages = pm.getPackagesForUid(uid); + if (sipper.mPackages == null) { + sipper.mPackages = pm.getPackagesForUid(uid); + } if (sipper.mPackages != null) { String[] packageLabels = new String[sipper.mPackages.length]; System.arraycopy(sipper.mPackages, 0, packageLabels, 0, sipper.mPackages.length); @@ -259,7 +280,7 @@ public class BatteryEntry { final ApplicationInfo ai = ipm.getApplicationInfo(packageLabels[i], 0 /* no flags */, userId); if (ai == null) { - Log.d(PowerUsageSummary.TAG, "Retrieving null app info for package " + Log.d(TAG, "Retrieving null app info for package " + packageLabels[i] + ", user " + userId); continue; } @@ -273,7 +294,7 @@ public class BatteryEntry { break; } } catch (RemoteException e) { - Log.d(PowerUsageSummary.TAG, "Error while retrieving app info for package " + Log.d(TAG, "Error while retrieving app info for package " + packageLabels[i] + ", user " + userId, e); } } @@ -286,7 +307,7 @@ public class BatteryEntry { try { final PackageInfo pi = ipm.getPackageInfo(pkgName, 0 /* no flags */, userId); if (pi == null) { - Log.d(PowerUsageSummary.TAG, "Retrieving null package info for package " + Log.d(TAG, "Retrieving null package info for package " + pkgName + ", user " + userId); continue; } @@ -303,7 +324,7 @@ public class BatteryEntry { } } } catch (RemoteException e) { - Log.d(PowerUsageSummary.TAG, "Error while retrieving package info for package " + Log.d(TAG, "Error while retrieving package info for package " + pkgName + ", user " + userId, e); } } diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryEntryTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryEntryTest.java new file mode 100644 index 00000000000..632d54926a7 --- /dev/null +++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryEntryTest.java @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2016 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.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.os.Handler; +import android.os.UserManager; + +import com.android.internal.os.BatterySipper; +import com.android.internal.os.BatterySipper.DrainType; +import com.android.settings.TestConfig; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoRule; +import org.mockito.junit.MockitoJUnit; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; + +import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@RunWith(RobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class BatteryEntryTest { + + private static final int APP_UID = 123; + private static final String APP_DEFAULT_PACKAGE_NAME = "com.android.test"; + private static final String APP_LABEL = "Test App Name"; + private static final String HIGH_DRAIN_PACKAGE = "com.android.test.screen"; + + @Rule public MockitoRule mocks = MockitoJUnit.rule(); + + @Mock private Context mockContext; + @Mock private Handler mockHandler; + @Mock private PackageManager mockPackageManager; + @Mock private UserManager mockUserManager; + + @Before + public void stubContextToReturnMockPackageManager() { + when(mockContext.getPackageManager()).thenReturn(mockPackageManager); + } + + @Before + public void stubPackageManagerToReturnAppPackageAndName() throws NameNotFoundException { + when(mockPackageManager.getPackagesForUid(APP_UID)).thenReturn( + new String[]{APP_DEFAULT_PACKAGE_NAME}); + + ApplicationInfo appInfo = mock(ApplicationInfo.class); + when(mockPackageManager.getApplicationInfo(APP_DEFAULT_PACKAGE_NAME, 0 /* no flags */)) + .thenReturn(appInfo); + when(mockPackageManager.getApplicationLabel(appInfo)).thenReturn(APP_LABEL); + } + + private BatteryEntry createBatteryEntryForApp() { + return new BatteryEntry(mockContext, mockHandler, mockUserManager, createSipperForApp()); + } + + private BatterySipper createSipperForApp() { + BatterySipper sipper = + new BatterySipper(DrainType.APP, new FakeUid(APP_UID), 0 /* power use */); + sipper.packageWithHighestDrain = HIGH_DRAIN_PACKAGE; + return sipper; + } + + @Test + public void batteryEntryForApp_shouldSetDefaultPackageNameAndLabel() throws Exception { + BatteryEntry entry = createBatteryEntryForApp(); + + assertThat(entry.defaultPackageName).isEqualTo(APP_DEFAULT_PACKAGE_NAME); + assertThat(entry.getLabel()).isEqualTo(APP_LABEL); + } + + @Test + public void batteryEntryForApp_shouldSetLabelAsPackageName_whenPackageCannotBeFound() + throws Exception { + when(mockPackageManager.getApplicationInfo(APP_DEFAULT_PACKAGE_NAME, 0 /* no flags */)) + .thenThrow(new NameNotFoundException()); + + BatteryEntry entry = createBatteryEntryForApp(); + + assertThat(entry.getLabel()).isEqualTo(APP_DEFAULT_PACKAGE_NAME); + } + + @Test + public void batteryEntryForApp_shouldSetHighestDrainPackage_whenPackagesCannotBeFoundForUid() { + when(mockPackageManager.getPackagesForUid(APP_UID)).thenReturn(null); + + BatteryEntry entry = createBatteryEntryForApp(); + + assertThat(entry.getLabel()).isEqualTo(HIGH_DRAIN_PACKAGE); + } + + @Test + public void batteryEntryForApp_shouldSetHighestDrainPackage_whenMultiplePackagesFoundForUid() { + when(mockPackageManager.getPackagesForUid(APP_UID)).thenReturn( + new String[]{APP_DEFAULT_PACKAGE_NAME, "package2", "package3"}); + + BatteryEntry entry = createBatteryEntryForApp(); + + assertThat(entry.getLabel()).isEqualTo(HIGH_DRAIN_PACKAGE); + } +}