1/* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.settings.applications; 18 19import android.app.AlertDialog; 20import android.app.LoaderManager; 21import android.app.admin.DevicePolicyManager; 22import android.content.Context; 23import android.content.Intent; 24import android.content.pm.ApplicationInfo; 25import android.content.pm.PackageInfo; 26import android.content.pm.PackageManager; 27import android.content.res.Resources; 28import android.os.BatteryStats; 29import android.os.Bundle; 30import android.os.UserManager; 31import android.support.v7.preference.Preference; 32import android.support.v7.preference.PreferenceManager; 33import android.support.v7.preference.PreferenceScreen; 34import android.view.View; 35import android.widget.Button; 36 37import com.android.internal.os.BatterySipper; 38import com.android.internal.os.BatteryStatsHelper; 39import com.android.settings.R; 40import com.android.settings.SettingsActivity; 41import com.android.settings.SettingsRobolectricTestRunner; 42import com.android.settings.TestConfig; 43import com.android.settings.applications.instantapps.InstantAppButtonsController; 44import com.android.settings.fuelgauge.BatteryUtils; 45import com.android.settings.testutils.FakeFeatureFactory; 46import com.android.settingslib.Utils; 47import com.android.settingslib.applications.AppUtils; 48import com.android.settingslib.applications.ApplicationsState.AppEntry; 49import com.android.settingslib.applications.StorageStatsSource.AppStorageStats; 50import com.android.settingslib.applications.instantapps.InstantAppDataProvider; 51 52import org.junit.Before; 53import org.junit.Test; 54import org.junit.runner.RunWith; 55import org.mockito.Answers; 56import org.mockito.Mock; 57import org.mockito.MockitoAnnotations; 58import org.robolectric.RuntimeEnvironment; 59import org.robolectric.annotation.Config; 60import org.robolectric.annotation.Implementation; 61import org.robolectric.annotation.Implements; 62import org.robolectric.util.ReflectionHelpers; 63 64import java.util.ArrayList; 65import java.util.HashSet; 66import java.util.List; 67 68import static com.google.common.truth.Truth.assertThat; 69import static org.mockito.Matchers.any; 70import static org.mockito.Matchers.anyDouble; 71import static org.mockito.Matchers.anyInt; 72import static org.mockito.Matchers.anyString; 73import static org.mockito.Mockito.doReturn; 74import static org.mockito.Mockito.mock; 75import static org.mockito.Mockito.never; 76import static org.mockito.Mockito.spy; 77import static org.mockito.Mockito.times; 78import static org.mockito.Mockito.verify; 79import static org.mockito.Mockito.when; 80 81 82@RunWith(SettingsRobolectricTestRunner.class) 83@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) 84public final class InstalledAppDetailsTest { 85 86 private static final String PACKAGE_NAME = "test_package_name"; 87 private static final int TARGET_UID = 111; 88 private static final int OTHER_UID = 222; 89 private static final double BATTERY_LEVEL = 60; 90 private static final String BATTERY_LEVEL_STRING = "60%"; 91 92 @Mock(answer = Answers.RETURNS_DEEP_STUBS) 93 private Context mContext; 94 @Mock(answer = Answers.RETURNS_DEEP_STUBS) 95 private UserManager mUserManager; 96 @Mock(answer = Answers.RETURNS_DEEP_STUBS) 97 private SettingsActivity mActivity; 98 @Mock 99 private DevicePolicyManager mDevicePolicyManager; 100 @Mock 101 private BatterySipper mBatterySipper; 102 @Mock 103 private BatterySipper mOtherBatterySipper; 104 @Mock(answer = Answers.RETURNS_DEEP_STUBS) 105 private BatteryStatsHelper mBatteryStatsHelper; 106 @Mock 107 private BatteryStats.Uid mUid; 108 @Mock 109 private PackageManager mPackageManager; 110 @Mock 111 private BatteryUtils mBatteryUtils; 112 @Mock 113 private LoaderManager mLoaderManager; 114 115 private FakeFeatureFactory mFeatureFactory; 116 private InstalledAppDetails mAppDetail; 117 private Context mShadowContext; 118 private Preference mBatteryPreference; 119 120 121 @Before 122 public void setUp() { 123 MockitoAnnotations.initMocks(this); 124 FakeFeatureFactory.setupForTest(mContext); 125 mFeatureFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext); 126 mShadowContext = RuntimeEnvironment.application; 127 mAppDetail = spy(new InstalledAppDetails()); 128 mAppDetail.mBatteryUtils = mBatteryUtils; 129 130 mBatteryPreference = new Preference(mShadowContext); 131 mAppDetail.mBatteryPreference = mBatteryPreference; 132 133 mBatterySipper.drainType = BatterySipper.DrainType.IDLE; 134 mBatterySipper.uidObj = mUid; 135 doReturn(TARGET_UID).when(mBatterySipper).getUid(); 136 doReturn(OTHER_UID).when(mOtherBatterySipper).getUid(); 137 doReturn(mActivity).when(mAppDetail).getActivity(); 138 doReturn(mShadowContext).when(mAppDetail).getContext(); 139 doReturn(mPackageManager).when(mActivity).getPackageManager(); 140 141 // Default to not considering any apps to be instant (individual tests can override this). 142 ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", 143 (InstantAppDataProvider) (i -> false)); 144 } 145 146 @Test 147 public void shouldShowUninstallForAll_installForOneOtherUserOnly_shouldReturnTrue() { 148 when(mDevicePolicyManager.packageHasActiveAdmins(anyString())).thenReturn(false); 149 when(mUserManager.getUsers().size()).thenReturn(2); 150 ReflectionHelpers.setField(mAppDetail, "mDpm", mDevicePolicyManager); 151 ReflectionHelpers.setField(mAppDetail, "mUserManager", mUserManager); 152 final ApplicationInfo info = new ApplicationInfo(); 153 info.enabled = true; 154 final AppEntry appEntry = mock(AppEntry.class); 155 appEntry.info = info; 156 final PackageInfo packageInfo = mock(PackageInfo.class); 157 ReflectionHelpers.setField(mAppDetail, "mPackageInfo", packageInfo); 158 159 assertThat(mAppDetail.shouldShowUninstallForAll(appEntry)).isTrue(); 160 } 161 162 @Test 163 public void shouldShowUninstallForAll_installForSelfOnly_shouldReturnFalse() { 164 when(mDevicePolicyManager.packageHasActiveAdmins(anyString())).thenReturn(false); 165 when(mUserManager.getUsers().size()).thenReturn(2); 166 ReflectionHelpers.setField(mAppDetail, "mDpm", mDevicePolicyManager); 167 ReflectionHelpers.setField(mAppDetail, "mUserManager", mUserManager); 168 final ApplicationInfo info = new ApplicationInfo(); 169 info.flags = ApplicationInfo.FLAG_INSTALLED; 170 info.enabled = true; 171 final AppEntry appEntry = mock(AppEntry.class); 172 appEntry.info = info; 173 final PackageInfo packageInfo = mock(PackageInfo.class); 174 ReflectionHelpers.setField(mAppDetail, "mPackageInfo", packageInfo); 175 176 assertThat(mAppDetail.shouldShowUninstallForAll(appEntry)).isFalse(); 177 } 178 179 @Test 180 public void getStorageSummary_shouldWorkForExternal() { 181 Context context = RuntimeEnvironment.application.getApplicationContext(); 182 AppStorageStats stats = mock(AppStorageStats.class); 183 when(stats.getTotalBytes()).thenReturn(1L); 184 185 assertThat(InstalledAppDetails.getStorageSummary(context, stats, true)) 186 .isEqualTo("1.00B used in external storage"); 187 } 188 189 @Test 190 public void getStorageSummary_shouldWorkForInternal() { 191 Context context = RuntimeEnvironment.application.getApplicationContext(); 192 AppStorageStats stats = mock(AppStorageStats.class); 193 when(stats.getTotalBytes()).thenReturn(1L); 194 195 assertThat(InstalledAppDetails.getStorageSummary(context, stats, false)) 196 .isEqualTo("1.00B used in internal storage"); 197 } 198 199 @Test 200 public void launchFragment_hasNoPackageInfo_shouldFinish() { 201 ReflectionHelpers.setField(mAppDetail, "mPackageInfo", null); 202 203 assertThat(mAppDetail.ensurePackageInfoAvailable(mActivity)).isFalse(); 204 verify(mActivity).finishAndRemoveTask(); 205 } 206 207 @Test 208 public void launchFragment_hasPackageInfo_shouldReturnTrue() { 209 final PackageInfo packageInfo = mock(PackageInfo.class); 210 ReflectionHelpers.setField(mAppDetail, "mPackageInfo", packageInfo); 211 212 assertThat(mAppDetail.ensurePackageInfoAvailable(mActivity)).isTrue(); 213 verify(mActivity, never()).finishAndRemoveTask(); 214 } 215 216 @Test 217 public void packageSizeChange_isOtherPackage_shouldNotRefreshUi() { 218 ReflectionHelpers.setField(mAppDetail, "mPackageName", PACKAGE_NAME); 219 mAppDetail.onPackageSizeChanged("Not_" + PACKAGE_NAME); 220 221 verify(mAppDetail, never()).refreshUi(); 222 } 223 224 @Test 225 public void packageSizeChange_isOwnPackage_shouldRefreshUi() { 226 doReturn(Boolean.TRUE).when(mAppDetail).refreshUi(); 227 ReflectionHelpers.setField(mAppDetail, "mPackageName", PACKAGE_NAME); 228 229 mAppDetail.onPackageSizeChanged(PACKAGE_NAME); 230 231 verify(mAppDetail).refreshUi(); 232 } 233 234 @Test 235 public void launchPowerUsageDetailFragment_shouldNotCrash() { 236 mAppDetail.mBatteryPreference = mBatteryPreference; 237 mAppDetail.mSipper = mBatterySipper; 238 mAppDetail.mBatteryHelper = mBatteryStatsHelper; 239 240 // Should not crash 241 mAppDetail.onPreferenceClick(mBatteryPreference); 242 } 243 244 // Tests that we don't show the "uninstall for all users" button for instant apps. 245 @Test 246 public void instantApps_noUninstallForAllButton() { 247 // Make this app appear to be instant. 248 ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", 249 (InstantAppDataProvider) (i -> true)); 250 when(mDevicePolicyManager.packageHasActiveAdmins(anyString())).thenReturn(false); 251 when(mUserManager.getUsers().size()).thenReturn(2); 252 253 final ApplicationInfo info = new ApplicationInfo(); 254 info.enabled = true; 255 final AppEntry appEntry = mock(AppEntry.class); 256 appEntry.info = info; 257 final PackageInfo packageInfo = mock(PackageInfo.class); 258 259 ReflectionHelpers.setField(mAppDetail, "mDpm", mDevicePolicyManager); 260 ReflectionHelpers.setField(mAppDetail, "mUserManager", mUserManager); 261 ReflectionHelpers.setField(mAppDetail, "mPackageInfo", packageInfo); 262 263 assertThat(mAppDetail.shouldShowUninstallForAll(appEntry)).isFalse(); 264 } 265 266 // Tests that we don't show the uninstall button for instant apps" 267 @Test 268 public void instantApps_noUninstallButton() { 269 // Make this app appear to be instant. 270 ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", 271 (InstantAppDataProvider) (i -> true)); 272 final ApplicationInfo info = new ApplicationInfo(); 273 info.flags = ApplicationInfo.FLAG_INSTALLED; 274 info.enabled = true; 275 final AppEntry appEntry = mock(AppEntry.class); 276 appEntry.info = info; 277 final PackageInfo packageInfo = mock(PackageInfo.class); 278 packageInfo.applicationInfo = info; 279 final Button uninstallButton = mock(Button.class); 280 281 ReflectionHelpers.setField(mAppDetail, "mUserManager", mUserManager); 282 ReflectionHelpers.setField(mAppDetail, "mAppEntry", appEntry); 283 ReflectionHelpers.setField(mAppDetail, "mPackageInfo", packageInfo); 284 ReflectionHelpers.setField(mAppDetail, "mUninstallButton", uninstallButton); 285 286 mAppDetail.initUnintsallButtonForUserApp(); 287 verify(uninstallButton).setVisibility(View.GONE); 288 } 289 290 // Tests that we don't show the force stop button for instant apps (they aren't allowed to run 291 // when they aren't in the foreground). 292 @Test 293 public void instantApps_noForceStop() { 294 // Make this app appear to be instant. 295 ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", 296 (InstantAppDataProvider) (i -> true)); 297 final PackageInfo packageInfo = mock(PackageInfo.class); 298 final AppEntry appEntry = mock(AppEntry.class); 299 final ApplicationInfo info = new ApplicationInfo(); 300 appEntry.info = info; 301 final Button forceStopButton = mock(Button.class); 302 303 ReflectionHelpers.setField(mAppDetail, "mDpm", mDevicePolicyManager); 304 ReflectionHelpers.setField(mAppDetail, "mPackageInfo", packageInfo); 305 ReflectionHelpers.setField(mAppDetail, "mAppEntry", appEntry); 306 ReflectionHelpers.setField(mAppDetail, "mForceStopButton", forceStopButton); 307 308 mAppDetail.checkForceStop(); 309 verify(forceStopButton).setVisibility(View.GONE); 310 } 311 312 @Test 313 public void instantApps_buttonControllerHandlesDialog() { 314 InstantAppButtonsController mockController = mock(InstantAppButtonsController.class); 315 ReflectionHelpers.setField( 316 mAppDetail, "mInstantAppButtonsController", mockController); 317 // Make sure first that button controller is not called for supported dialog id 318 AlertDialog mockDialog = mock(AlertDialog.class); 319 when(mockController.createDialog(InstantAppButtonsController.DLG_CLEAR_APP)) 320 .thenReturn(mockDialog); 321 assertThat(mAppDetail.createDialog(InstantAppButtonsController.DLG_CLEAR_APP, 0)) 322 .isEqualTo(mockDialog); 323 verify(mockController).createDialog(InstantAppButtonsController.DLG_CLEAR_APP); 324 } 325 326 // A helper class for testing the InstantAppButtonsController - it lets us look up the 327 // preference associated with a key for instant app buttons and get back a mock 328 // LayoutPreference (to avoid a null pointer exception). 329 public static class InstalledAppDetailsWithMockInstantButtons extends InstalledAppDetails { 330 @Mock 331 private LayoutPreference mInstantButtons; 332 333 public InstalledAppDetailsWithMockInstantButtons() { 334 super(); 335 MockitoAnnotations.initMocks(this); 336 } 337 338 @Override 339 public Preference findPreference(CharSequence key) { 340 if (key == "instant_app_buttons") { 341 return mInstantButtons; 342 } 343 return super.findPreference(key); 344 } 345 } 346 347 @Test 348 public void instantApps_instantSpecificButtons() { 349 // Make this app appear to be instant. 350 ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", 351 (InstantAppDataProvider) (i -> true)); 352 final PackageInfo packageInfo = mock(PackageInfo.class); 353 354 final InstalledAppDetailsWithMockInstantButtons 355 fragment = new InstalledAppDetailsWithMockInstantButtons(); 356 ReflectionHelpers.setField(fragment, "mPackageInfo", packageInfo); 357 ReflectionHelpers.setField(fragment, "mApplicationFeatureProvider", 358 mFeatureFactory.applicationFeatureProvider); 359 360 final InstantAppButtonsController buttonsController = 361 mock(InstantAppButtonsController.class); 362 when(buttonsController.setPackageName(anyString())).thenReturn(buttonsController); 363 364 when(mFeatureFactory.applicationFeatureProvider.newInstantAppButtonsController( 365 any(), any(), any())).thenReturn(buttonsController); 366 367 fragment.maybeAddInstantAppButtons(); 368 verify(buttonsController).setPackageName(anyString()); 369 verify(buttonsController).show(); 370 } 371 372 @Test 373 public void instantApps_removeCorrectPref() { 374 PreferenceScreen mockPreferenceScreen = mock(PreferenceScreen.class); 375 PreferenceManager mockPreferenceManager = mock(PreferenceManager.class); 376 AppDomainsPreference mockAppDomainsPref = mock(AppDomainsPreference.class); 377 Preference mockLaunchPreference = mock(Preference.class); 378 PackageInfo mockPackageInfo = mock(PackageInfo.class); 379 PackageManager mockPackageManager = mock(PackageManager.class); 380 ReflectionHelpers.setField( 381 mAppDetail, "mLaunchPreference", mockLaunchPreference); 382 ReflectionHelpers.setField( 383 mAppDetail, "mInstantAppDomainsPreference", mockAppDomainsPref); 384 ReflectionHelpers.setField( 385 mAppDetail, "mPreferenceManager", mockPreferenceManager); 386 ReflectionHelpers.setField( 387 mAppDetail, "mPackageInfo", mockPackageInfo); 388 ReflectionHelpers.setField( 389 mAppDetail, "mPm", mockPackageManager); 390 when(mockPreferenceManager.getPreferenceScreen()).thenReturn(mockPreferenceScreen); 391 392 ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", 393 (InstantAppDataProvider) (i -> false)); 394 mAppDetail.prepareInstantAppPrefs(); 395 396 // For the non instant case we remove the app domain pref, and leave the launch pref 397 verify(mockPreferenceScreen).removePreference(mockAppDomainsPref); 398 verify(mockPreferenceScreen, never()).removePreference(mockLaunchPreference); 399 400 // For the instant app case we remove the launch preff, and leave the app domain pref 401 ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", 402 (InstantAppDataProvider) (i -> true)); 403 404 mAppDetail.prepareInstantAppPrefs(); 405 verify(mockPreferenceScreen).removePreference(mockLaunchPreference); 406 // Will be 1 still due to above call 407 verify(mockPreferenceScreen, times(1)) 408 .removePreference(mockAppDomainsPref); 409 } 410 411 @Test 412 public void onActivityResult_uninstalledUpdates_shouldInvalidateOptionsMenu() { 413 doReturn(true).when(mAppDetail).refreshUi(); 414 415 mAppDetail.onActivityResult(InstalledAppDetails.REQUEST_UNINSTALL, 0, mock(Intent.class)); 416 417 verify(mActivity).invalidateOptionsMenu(); 418 } 419 420 @Test 421 public void findTargetSipper_findCorrectSipper() { 422 List<BatterySipper> usageList = new ArrayList<>(); 423 usageList.add(mBatterySipper); 424 usageList.add(mOtherBatterySipper); 425 doReturn(usageList).when(mBatteryStatsHelper).getUsageList(); 426 427 assertThat(mAppDetail.findTargetSipper(mBatteryStatsHelper, TARGET_UID)).isEqualTo( 428 mBatterySipper); 429 } 430 431 @Test 432 public void updateBattery_noBatteryStats_summaryNo() { 433 doReturn(mShadowContext.getString(R.string.no_battery_summary)).when(mAppDetail).getString( 434 R.string.no_battery_summary); 435 mAppDetail.updateBattery(); 436 437 assertThat(mBatteryPreference.getSummary()).isEqualTo( 438 "No battery use since last full charge"); 439 } 440 441 @Test 442 public void updateBattery_hasBatteryStats_summaryPercent() { 443 mAppDetail.mBatteryHelper = mBatteryStatsHelper; 444 mAppDetail.mSipper = mBatterySipper; 445 doReturn(BATTERY_LEVEL).when(mBatteryUtils).calculateBatteryPercent(anyDouble(), 446 anyDouble(), anyDouble(), anyInt()); 447 doReturn(mShadowContext.getString(R.string.battery_summary, BATTERY_LEVEL_STRING)).when( 448 mAppDetail).getString(R.string.battery_summary, BATTERY_LEVEL_STRING); 449 doReturn(new ArrayList<>()).when(mBatteryStatsHelper).getUsageList(); 450 451 mAppDetail.updateBattery(); 452 453 assertThat(mBatteryPreference.getSummary()).isEqualTo("60% use since last full charge"); 454 } 455 456 @Test 457 public void isBatteryStatsAvailable_hasBatteryStatsHelperAndSipper_returnTrue() { 458 mAppDetail.mBatteryHelper = mBatteryStatsHelper; 459 mAppDetail.mSipper = mBatterySipper; 460 461 assertThat(mAppDetail.isBatteryStatsAvailable()).isTrue(); 462 } 463 464 @Test 465 public void isBatteryStatsAvailable_parametersNull_returnFalse() { 466 assertThat(mAppDetail.isBatteryStatsAvailable()).isFalse(); 467 } 468 469 @Test 470 public void handleDisableable_appIsHomeApp_buttonShouldNotWork() { 471 final ApplicationInfo info = new ApplicationInfo(); 472 info.packageName = "pkg"; 473 info.enabled = true; 474 final AppEntry appEntry = mock(AppEntry.class); 475 appEntry.info = info; 476 final HashSet<String> homePackages = new HashSet<>(); 477 homePackages.add(info.packageName); 478 479 ReflectionHelpers.setField(mAppDetail, "mHomePackages", homePackages); 480 ReflectionHelpers.setField(mAppDetail, "mAppEntry", appEntry); 481 final Button button = mock(Button.class); 482 483 assertThat(mAppDetail.handleDisableable(button)).isFalse(); 484 verify(button).setText(R.string.disable_text); 485 } 486 487 @Test 488 @Config(shadows = ShadowUtils.class) 489 public void handleDisableable_appIsEnabled_buttonShouldWork() { 490 final ApplicationInfo info = new ApplicationInfo(); 491 info.packageName = "pkg"; 492 info.enabled = true; 493 info.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_ENABLED; 494 495 final AppEntry appEntry = mock(AppEntry.class); 496 appEntry.info = info; 497 when(mFeatureFactory.applicationFeatureProvider.getKeepEnabledPackages()).thenReturn( 498 new HashSet<>()); 499 500 ReflectionHelpers.setField(mAppDetail, "mApplicationFeatureProvider", 501 mFeatureFactory.applicationFeatureProvider); 502 ReflectionHelpers.setField(mAppDetail, "mAppEntry", appEntry); 503 final Button button = mock(Button.class); 504 505 assertThat(mAppDetail.handleDisableable(button)).isTrue(); 506 verify(button).setText(R.string.disable_text); 507 } 508 509 @Test 510 @Config(shadows = ShadowUtils.class) 511 public void handleDisableable_appIsEnabledAndInKeepEnabledWhitelist_buttonShouldNotWork() { 512 final ApplicationInfo info = new ApplicationInfo(); 513 info.packageName = "pkg"; 514 info.enabled = true; 515 info.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_ENABLED; 516 517 final AppEntry appEntry = mock(AppEntry.class); 518 appEntry.info = info; 519 520 final HashSet<String> packages = new HashSet<>(); 521 packages.add(info.packageName); 522 when(mFeatureFactory.applicationFeatureProvider.getKeepEnabledPackages()).thenReturn( 523 packages); 524 525 ReflectionHelpers.setField(mAppDetail, "mApplicationFeatureProvider", 526 mFeatureFactory.applicationFeatureProvider); 527 ReflectionHelpers.setField(mAppDetail, "mAppEntry", appEntry); 528 529 final Button button = mock(Button.class); 530 531 assertThat(mAppDetail.handleDisableable(button)).isFalse(); 532 verify(button).setText(R.string.disable_text); 533 } 534 535 @Test 536 public void testRestartBatteryStatsLoader() { 537 doReturn(mLoaderManager).when(mAppDetail).getLoaderManager(); 538 539 mAppDetail.restartBatteryStatsLoader(); 540 541 verify(mLoaderManager).restartLoader(InstalledAppDetails.LOADER_BATTERY, Bundle.EMPTY, 542 mAppDetail.mBatteryCallbacks); 543 } 544 545 @Implements(Utils.class) 546 public static class ShadowUtils { 547 @Implementation 548 public static boolean isSystemPackage(Resources resources, PackageManager pm, 549 PackageInfo pkg) { 550 return false; 551 } 552 } 553} 554