1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 * use this file except in compliance with the License. You may obtain a copy 6 * 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, WITHOUT 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 * License for the specific language governing permissions and limitations 14 * under the License. 15 */ 16 17 package com.android.settings.applications.appinfo; 18 19 import static android.app.admin.DevicePolicyResources.Strings.Settings.CONNECTED_WORK_AND_PERSONAL_APPS_TITLE; 20 21 import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; 22 23 import android.app.Activity; 24 import android.app.AppOpsManager; 25 import android.app.KeyguardManager; 26 import android.app.admin.DevicePolicyManager; 27 import android.app.settings.SettingsEnums; 28 import android.content.BroadcastReceiver; 29 import android.content.Context; 30 import android.content.Intent; 31 import android.content.IntentFilter; 32 import android.content.pm.ApplicationInfo; 33 import android.content.pm.PackageInfo; 34 import android.content.pm.PackageManager; 35 import android.content.pm.PackageManager.NameNotFoundException; 36 import android.content.pm.UserInfo; 37 import android.hardware.biometrics.BiometricManager; 38 import android.hardware.biometrics.BiometricPrompt; 39 import android.net.Uri; 40 import android.os.Bundle; 41 import android.os.CancellationSignal; 42 import android.os.Handler; 43 import android.os.Looper; 44 import android.os.UserHandle; 45 import android.os.UserManager; 46 import android.text.TextUtils; 47 import android.util.Log; 48 import android.view.Menu; 49 import android.view.MenuInflater; 50 import android.view.MenuItem; 51 import android.widget.Toast; 52 53 import androidx.annotation.VisibleForTesting; 54 55 import com.android.settings.R; 56 import com.android.settings.SettingsActivity; 57 import com.android.settings.SettingsPreferenceFragment; 58 import com.android.settings.applications.manageapplications.ManageApplications; 59 import com.android.settings.applications.specialaccess.interactacrossprofiles.InteractAcrossProfilesDetailsPreferenceController; 60 import com.android.settings.applications.specialaccess.pictureinpicture.PictureInPictureDetailPreferenceController; 61 import com.android.settings.core.SubSettingLauncher; 62 import com.android.settings.dashboard.DashboardFragment; 63 import com.android.settingslib.RestrictedLockUtilsInternal; 64 import com.android.settingslib.applications.AppUtils; 65 import com.android.settingslib.applications.ApplicationsState; 66 import com.android.settingslib.applications.ApplicationsState.AppEntry; 67 import com.android.settingslib.core.AbstractPreferenceController; 68 import com.android.settingslib.core.lifecycle.Lifecycle; 69 70 import java.util.ArrayList; 71 import java.util.Arrays; 72 import java.util.List; 73 74 /** 75 * Dashboard fragment to display application information from Settings. This activity presents 76 * extended information associated with a package like code, data, total size, permissions 77 * used by the application and also the set of default launchable activities. 78 * For system applications, an option to clear user data is displayed only if data size is > 0. 79 * System applications that do not want clear user data do not have this option. 80 * For non-system applications, there is no option to clear data. Instead there is an option to 81 * uninstall the application. 82 */ 83 public class AppInfoDashboardFragment extends DashboardFragment 84 implements ApplicationsState.Callbacks, 85 ButtonActionDialogFragment.AppButtonsDialogListener { 86 87 private static final String TAG = "AppInfoDashboard"; 88 89 // Menu identifiers 90 @VisibleForTesting 91 static final int UNINSTALL_ALL_USERS_MENU = 1; 92 @VisibleForTesting 93 static final int UNINSTALL_UPDATES = 2; 94 static final int INSTALL_INSTANT_APP_MENU = 3; 95 static final int ACCESS_RESTRICTED_SETTINGS = 4; 96 97 // Result code identifiers 98 @VisibleForTesting 99 static final int REQUEST_UNINSTALL = 0; 100 private static final int REQUEST_REMOVE_DEVICE_ADMIN = 5; 101 102 static final int SUB_INFO_FRAGMENT = 1; 103 104 static final int LOADER_CHART_DATA = 2; 105 static final int LOADER_STORAGE = 3; 106 static final int LOADER_BATTERY = 4; 107 static final int LOADER_BATTERY_USAGE_STATS = 5; 108 109 public static final String ARG_PACKAGE_NAME = "package"; 110 public static final String ARG_PACKAGE_UID = "uid"; 111 112 private static final boolean localLOGV = false; 113 114 private EnforcedAdmin mAppsControlDisallowedAdmin; 115 private boolean mAppsControlDisallowedBySystem; 116 117 private ApplicationsState mState; 118 private ApplicationsState.Session mSession; 119 private ApplicationsState.AppEntry mAppEntry; 120 private PackageInfo mPackageInfo; 121 private int mUserId; 122 private String mPackageName; 123 private int mUid; 124 125 private DevicePolicyManager mDpm; 126 private UserManager mUserManager; 127 private PackageManager mPm; 128 129 @VisibleForTesting 130 boolean mFinishing; 131 private boolean mListeningToPackageRemove; 132 133 134 private boolean mInitialized; 135 private boolean mShowUninstalled; 136 private boolean mUpdatedSysApp = false; 137 138 private List<Callback> mCallbacks = new ArrayList<>(); 139 140 private InstantAppButtonsPreferenceController mInstantAppButtonPreferenceController; 141 private AppButtonsPreferenceController mAppButtonsPreferenceController; 142 143 /** 144 * Callback to invoke when app info has been changed. 145 */ 146 public interface Callback { refreshUi()147 void refreshUi(); 148 } 149 150 @Override onAttach(Context context)151 public void onAttach(Context context) { 152 super.onAttach(context); 153 final String packageName = getPackageName(); 154 final TimeSpentInAppPreferenceController timeSpentInAppPreferenceController = use( 155 TimeSpentInAppPreferenceController.class); 156 timeSpentInAppPreferenceController.setPackageName(packageName); 157 timeSpentInAppPreferenceController.setParentFragment(this); 158 timeSpentInAppPreferenceController.initLifeCycleOwner(this); 159 160 use(AppDataUsagePreferenceController.class).setParentFragment(this); 161 final AppInstallerInfoPreferenceController installer = 162 use(AppInstallerInfoPreferenceController.class); 163 installer.setPackageName(packageName); 164 installer.setParentFragment(this); 165 use(AppInstallerPreferenceCategoryController.class).setChildren(Arrays.asList(installer)); 166 use(AppNotificationPreferenceController.class).setParentFragment(this); 167 168 use(AppOpenByDefaultPreferenceController.class) 169 .setPackageName(packageName) 170 .setParentFragment(this); 171 172 use(AppPermissionPreferenceController.class).setParentFragment(this); 173 use(AppPermissionPreferenceController.class).setPackageName(packageName); 174 use(AppSettingPreferenceController.class) 175 .setPackageName(packageName) 176 .setParentFragment(this); 177 use(AppAllServicesPreferenceController.class).setParentFragment(this); 178 use(AppAllServicesPreferenceController.class).setPackageName(packageName); 179 use(AppStoragePreferenceController.class).setParentFragment(this); 180 use(AppVersionPreferenceController.class).setParentFragment(this); 181 use(InstantAppDomainsPreferenceController.class).setParentFragment(this); 182 183 final HibernationSwitchPreferenceController appHibernationSettings = 184 use(HibernationSwitchPreferenceController.class); 185 appHibernationSettings.setParentFragment(this); 186 appHibernationSettings.setPackage(packageName); 187 use(AppHibernationPreferenceCategoryController.class).setChildren( 188 Arrays.asList(appHibernationSettings)); 189 190 final WriteSystemSettingsPreferenceController writeSystemSettings = 191 use(WriteSystemSettingsPreferenceController.class); 192 writeSystemSettings.setParentFragment(this); 193 194 final DrawOverlayDetailPreferenceController drawOverlay = 195 use(DrawOverlayDetailPreferenceController.class); 196 drawOverlay.setParentFragment(this); 197 198 final PictureInPictureDetailPreferenceController pip = 199 use(PictureInPictureDetailPreferenceController.class); 200 pip.setPackageName(packageName); 201 pip.setParentFragment(this); 202 203 final ExternalSourceDetailPreferenceController externalSource = 204 use(ExternalSourceDetailPreferenceController.class); 205 externalSource.setPackageName(packageName); 206 externalSource.setParentFragment(this); 207 208 final InteractAcrossProfilesDetailsPreferenceController acrossProfiles = 209 use(InteractAcrossProfilesDetailsPreferenceController.class); 210 acrossProfiles.setPackageName(packageName); 211 acrossProfiles.setParentFragment(this); 212 213 final AlarmsAndRemindersDetailPreferenceController alarmsAndReminders = 214 use(AlarmsAndRemindersDetailPreferenceController.class); 215 alarmsAndReminders.setPackageName(packageName); 216 alarmsAndReminders.setParentFragment(this); 217 218 final LongBackgroundTasksDetailsPreferenceController longBackgroundTasks = 219 use(LongBackgroundTasksDetailsPreferenceController.class); 220 longBackgroundTasks.setPackageName(packageName); 221 longBackgroundTasks.setParentFragment(this); 222 223 final AdvancedAppInfoPreferenceCategoryController advancedAppInfo = 224 use(AdvancedAppInfoPreferenceCategoryController.class); 225 advancedAppInfo.setChildren(Arrays.asList(writeSystemSettings, drawOverlay, pip, 226 externalSource, acrossProfiles, alarmsAndReminders, longBackgroundTasks)); 227 advancedAppInfo.setAppEntry(mAppEntry); 228 229 final AppLocalePreferenceController appLocale = 230 use(AppLocalePreferenceController.class); 231 appLocale.setParentFragment(this); 232 } 233 234 @Override onCreate(Bundle icicle)235 public void onCreate(Bundle icicle) { 236 super.onCreate(icicle); 237 mFinishing = false; 238 final Activity activity = getActivity(); 239 mDpm = (DevicePolicyManager) activity.getSystemService(Context.DEVICE_POLICY_SERVICE); 240 mUserManager = (UserManager) activity.getSystemService(Context.USER_SERVICE); 241 mPm = activity.getPackageManager(); 242 if (!ensurePackageInfoAvailable(activity)) { 243 return; 244 } 245 if (!ensureDisplayableModule(activity)) { 246 return; 247 } 248 startListeningToPackageRemove(); 249 250 setHasOptionsMenu(true); 251 replaceEnterpriseStringTitle("interact_across_profiles", 252 CONNECTED_WORK_AND_PERSONAL_APPS_TITLE, R.string.interact_across_profiles_title); 253 } 254 255 @Override onCreatePreferences(Bundle savedInstanceState, String rootKey)256 public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { 257 if (!ensurePackageInfoAvailable(getActivity())) { 258 return; 259 } 260 super.onCreatePreferences(savedInstanceState, rootKey); 261 } 262 263 @Override onDestroy()264 public void onDestroy() { 265 stopListeningToPackageRemove(); 266 super.onDestroy(); 267 } 268 269 @Override getMetricsCategory()270 public int getMetricsCategory() { 271 return SettingsEnums.APPLICATIONS_INSTALLED_APP_DETAILS; 272 } 273 274 @Override onResume()275 public void onResume() { 276 super.onResume(); 277 final Activity activity = getActivity(); 278 mAppsControlDisallowedAdmin = RestrictedLockUtilsInternal.checkIfRestrictionEnforced( 279 activity, UserManager.DISALLOW_APPS_CONTROL, mUserId); 280 mAppsControlDisallowedBySystem = RestrictedLockUtilsInternal.hasBaseUserRestriction( 281 activity, UserManager.DISALLOW_APPS_CONTROL, mUserId); 282 283 if (!refreshUi()) { 284 setIntentAndFinish(true, true); 285 } 286 getActivity().invalidateOptionsMenu(); 287 } 288 289 @Override getPreferenceScreenResId()290 protected int getPreferenceScreenResId() { 291 return R.xml.app_info_settings; 292 } 293 294 @Override getLogTag()295 protected String getLogTag() { 296 return TAG; 297 } 298 299 @Override createPreferenceControllers(Context context)300 protected List<AbstractPreferenceController> createPreferenceControllers(Context context) { 301 retrieveAppEntry(); 302 if (mPackageInfo == null) { 303 return null; 304 } 305 final String packageName = getPackageName(); 306 final List<AbstractPreferenceController> controllers = new ArrayList<>(); 307 final Lifecycle lifecycle = getSettingsLifecycle(); 308 309 // The following are controllers for preferences that needs to refresh the preference state 310 // when app state changes. 311 controllers.add( 312 new AppHeaderViewPreferenceController(context, this, packageName, lifecycle)); 313 314 for (AbstractPreferenceController controller : controllers) { 315 mCallbacks.add((Callback) controller); 316 } 317 318 // The following are controllers for preferences that don't need to refresh the preference 319 // state when app state changes. 320 mInstantAppButtonPreferenceController = 321 new InstantAppButtonsPreferenceController(context, this, packageName, lifecycle); 322 controllers.add(mInstantAppButtonPreferenceController); 323 mAppButtonsPreferenceController = new AppButtonsPreferenceController( 324 (SettingsActivity) getActivity(), this, lifecycle, packageName, mState, 325 REQUEST_UNINSTALL, REQUEST_REMOVE_DEVICE_ADMIN); 326 controllers.add(mAppButtonsPreferenceController); 327 controllers.add(new AppBatteryPreferenceController( 328 context, this, packageName, getUid(), lifecycle)); 329 controllers.add(new AppMemoryPreferenceController(context, this, lifecycle)); 330 controllers.add(new DefaultHomeShortcutPreferenceController(context, packageName)); 331 controllers.add(new DefaultBrowserShortcutPreferenceController(context, packageName)); 332 controllers.add(new DefaultPhoneShortcutPreferenceController(context, packageName)); 333 controllers.add(new DefaultEmergencyShortcutPreferenceController(context, packageName)); 334 controllers.add(new DefaultSmsShortcutPreferenceController(context, packageName)); 335 336 return controllers; 337 } 338 addToCallbackList(Callback callback)339 void addToCallbackList(Callback callback) { 340 if (callback != null) { 341 mCallbacks.add(callback); 342 } 343 } 344 getAppEntry()345 ApplicationsState.AppEntry getAppEntry() { 346 return mAppEntry; 347 } 348 setAppEntry(ApplicationsState.AppEntry appEntry)349 void setAppEntry(ApplicationsState.AppEntry appEntry) { 350 mAppEntry = appEntry; 351 } 352 getPackageInfo()353 public PackageInfo getPackageInfo() { 354 return mPackageInfo; 355 } 356 357 @Override onPackageSizeChanged(String packageName)358 public void onPackageSizeChanged(String packageName) { 359 if (!TextUtils.equals(packageName, mPackageName)) { 360 Log.d(TAG, "Package change irrelevant, skipping"); 361 return; 362 } 363 refreshUi(); 364 } 365 366 /** 367 * Ensures the {@link PackageInfo} is available to proceed. If it's not available, the fragment 368 * will finish. 369 * 370 * @return true if packageInfo is available. 371 */ 372 @VisibleForTesting ensurePackageInfoAvailable(Activity activity)373 boolean ensurePackageInfoAvailable(Activity activity) { 374 if (mPackageInfo == null) { 375 mFinishing = true; 376 Log.w(TAG, "Package info not available. Is this package already uninstalled?"); 377 activity.finishAndRemoveTask(); 378 return false; 379 } 380 return true; 381 } 382 383 /** 384 * Ensures the package is displayable as directed by {@link AppUtils#isHiddenSystemModule}. 385 * If it's not, the fragment will finish. 386 * 387 * @return true if package is displayable. 388 */ 389 @VisibleForTesting ensureDisplayableModule(Activity activity)390 boolean ensureDisplayableModule(Activity activity) { 391 if (AppUtils.isHiddenSystemModule(activity.getApplicationContext(), mPackageName)) { 392 mFinishing = true; 393 Log.w(TAG, "Package is hidden module, exiting: " + mPackageName); 394 activity.finishAndRemoveTask(); 395 return false; 396 } 397 return true; 398 } 399 400 @Override onCreateOptionsMenu(Menu menu, MenuInflater inflater)401 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 402 super.onCreateOptionsMenu(menu, inflater); 403 menu.add(0, UNINSTALL_UPDATES, 0, R.string.app_factory_reset) 404 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); 405 menu.add(0, UNINSTALL_ALL_USERS_MENU, 1, R.string.uninstall_all_users_text) 406 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); 407 menu.add(0, ACCESS_RESTRICTED_SETTINGS, 0, 408 R.string.app_restricted_settings_lockscreen_title) 409 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); 410 } 411 412 @Override onPrepareOptionsMenu(Menu menu)413 public void onPrepareOptionsMenu(Menu menu) { 414 if (mFinishing) { 415 return; 416 } 417 super.onPrepareOptionsMenu(menu); 418 final MenuItem uninstallAllUsersItem = menu.findItem(UNINSTALL_ALL_USERS_MENU); 419 uninstallAllUsersItem.setVisible( 420 shouldShowUninstallForAll(mAppEntry) && !mAppsControlDisallowedBySystem); 421 if (uninstallAllUsersItem.isVisible()) { 422 RestrictedLockUtilsInternal.setMenuItemAsDisabledByAdmin(getActivity(), 423 uninstallAllUsersItem, mAppsControlDisallowedAdmin); 424 } 425 menu.findItem(ACCESS_RESTRICTED_SETTINGS).setVisible(shouldShowAccessRestrictedSettings()); 426 mUpdatedSysApp = (mAppEntry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0; 427 final MenuItem uninstallUpdatesItem = menu.findItem(UNINSTALL_UPDATES); 428 final boolean uninstallUpdateDisabled = getContext().getResources().getBoolean( 429 R.bool.config_disable_uninstall_update); 430 uninstallUpdatesItem.setVisible(mUserManager.isAdminUser() 431 && mUpdatedSysApp 432 && !mAppsControlDisallowedBySystem 433 && !uninstallUpdateDisabled); 434 if (uninstallUpdatesItem.isVisible()) { 435 RestrictedLockUtilsInternal.setMenuItemAsDisabledByAdmin(getActivity(), 436 uninstallUpdatesItem, mAppsControlDisallowedAdmin); 437 } 438 } 439 440 /** Shows the lock screen if the keyguard is secured. */ showLockScreen(Context context, Runnable successRunnable)441 public static void showLockScreen(Context context, Runnable successRunnable) { 442 final KeyguardManager keyguardManager = context.getSystemService( 443 KeyguardManager.class); 444 445 if (keyguardManager.isKeyguardSecure()) { 446 final BiometricPrompt.AuthenticationCallback authenticationCallback = 447 new BiometricPrompt.AuthenticationCallback() { 448 @Override 449 public void onAuthenticationSucceeded( 450 BiometricPrompt.AuthenticationResult result) { 451 successRunnable.run(); 452 } 453 454 @Override 455 public void onAuthenticationError(int errorCode, CharSequence errString) { 456 //Do nothing 457 } 458 }; 459 460 final BiometricPrompt.Builder builder = new BiometricPrompt.Builder(context) 461 .setUseDefaultSubtitle() // use default subtitle if subtitle is null/empty 462 .setUseDefaultTitle(); // use default title if title is null/empty 463 464 final BiometricManager bm = context.getSystemService(BiometricManager.class); 465 final int authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL 466 | BiometricManager.Authenticators.BIOMETRIC_WEAK; 467 if (bm.canAuthenticate(authenticators) == BiometricManager.BIOMETRIC_SUCCESS) { 468 builder.setAllowedAuthenticators(authenticators); 469 builder.setSubtitle(bm.getStrings(authenticators).getPromptMessage()); 470 } 471 472 final BiometricPrompt bp = builder.build(); 473 final Handler handler = new Handler(Looper.getMainLooper()); 474 bp.authenticate(new CancellationSignal(), 475 runnable -> handler.post(runnable), 476 authenticationCallback); 477 } else { 478 successRunnable.run(); 479 } 480 } 481 482 @Override onOptionsItemSelected(MenuItem item)483 public boolean onOptionsItemSelected(MenuItem item) { 484 switch (item.getItemId()) { 485 case UNINSTALL_ALL_USERS_MENU: 486 uninstallPkg(mAppEntry.info.packageName, true, false); 487 return true; 488 case UNINSTALL_UPDATES: 489 uninstallPkg(mAppEntry.info.packageName, false, false); 490 return true; 491 case ACCESS_RESTRICTED_SETTINGS: 492 showLockScreen(getContext(), () -> { 493 final AppOpsManager appOpsManager = getContext().getSystemService( 494 AppOpsManager.class); 495 appOpsManager.setMode(AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS, 496 getUid(), 497 getPackageName(), 498 AppOpsManager.MODE_ALLOWED); 499 getActivity().invalidateOptionsMenu(); 500 final String toastString = getContext().getString( 501 R.string.toast_allows_restricted_settings_successfully, 502 mAppEntry.label); 503 Toast.makeText(getContext(), toastString, Toast.LENGTH_LONG).show(); 504 }); 505 return true; 506 } 507 return super.onOptionsItemSelected(item); 508 } 509 510 @Override onActivityResult(int requestCode, int resultCode, Intent data)511 public void onActivityResult(int requestCode, int resultCode, Intent data) { 512 super.onActivityResult(requestCode, resultCode, data); 513 if (requestCode == REQUEST_UNINSTALL) { 514 // Refresh option menu 515 getActivity().invalidateOptionsMenu(); 516 } 517 if (mAppButtonsPreferenceController != null) { 518 mAppButtonsPreferenceController.handleActivityResult(requestCode, resultCode, data); 519 } 520 } 521 522 @Override handleDialogClick(int id)523 public void handleDialogClick(int id) { 524 if (mAppButtonsPreferenceController != null) { 525 mAppButtonsPreferenceController.handleDialogClick(id); 526 } 527 } 528 shouldShowAccessRestrictedSettings()529 private boolean shouldShowAccessRestrictedSettings() { 530 try { 531 final int mode = getSystemService(AppOpsManager.class).noteOpNoThrow( 532 AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS, getUid(), 533 getPackageName()); 534 return mode == AppOpsManager.MODE_IGNORED; 535 } catch (Exception e) { 536 // Fallback in case if app ops is not available in testing. 537 return false; 538 } 539 } 540 541 @VisibleForTesting shouldShowUninstallForAll(AppEntry appEntry)542 boolean shouldShowUninstallForAll(AppEntry appEntry) { 543 boolean showIt = true; 544 if (mUpdatedSysApp) { 545 showIt = false; 546 } else if (appEntry == null) { 547 showIt = false; 548 } else if ((appEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { 549 showIt = false; 550 } else if (mPackageInfo == null || mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) { 551 showIt = false; 552 } else if (UserHandle.myUserId() != 0) { 553 showIt = false; 554 } else if (mUserManager.getUsers().size() < 2) { 555 showIt = false; 556 } else if (getNumberOfUserWithPackageInstalled(mPackageName) < 2 557 && (appEntry.info.flags & ApplicationInfo.FLAG_INSTALLED) != 0) { 558 showIt = false; 559 } else if (AppUtils.isInstant(appEntry.info)) { 560 showIt = false; 561 } 562 return showIt; 563 } 564 565 @VisibleForTesting refreshUi()566 boolean refreshUi() { 567 retrieveAppEntry(); 568 if (mAppEntry == null) { 569 return false; // onCreate must have failed, make sure to exit 570 } 571 572 if (mPackageInfo == null) { 573 return false; // onCreate must have failed, make sure to exit 574 } 575 576 mState.ensureIcon(mAppEntry); 577 578 // Update the preference summaries. 579 for (Callback callback : mCallbacks) { 580 callback.refreshUi(); 581 } 582 if (mAppButtonsPreferenceController.isAvailable()) { 583 mAppButtonsPreferenceController.refreshUi(); 584 } 585 586 if (!mInitialized) { 587 // First time init: are we displaying an uninstalled app? 588 mInitialized = true; 589 mShowUninstalled = (mAppEntry.info.flags & ApplicationInfo.FLAG_INSTALLED) == 0; 590 } else { 591 // All other times: if the app no longer exists then we want 592 // to go away. 593 try { 594 final ApplicationInfo ainfo = getActivity().getPackageManager().getApplicationInfo( 595 mAppEntry.info.packageName, 596 PackageManager.MATCH_DISABLED_COMPONENTS 597 | PackageManager.MATCH_ANY_USER); 598 if (!mShowUninstalled) { 599 // If we did not start out with the app uninstalled, then 600 // it transitioning to the uninstalled state for the current 601 // user means we should go away as well. 602 return (ainfo.flags & ApplicationInfo.FLAG_INSTALLED) != 0; 603 } 604 } catch (NameNotFoundException e) { 605 return false; 606 } 607 } 608 609 return true; 610 } 611 612 @Override shouldSkipForInitialSUW()613 protected boolean shouldSkipForInitialSUW() { 614 return true; 615 } 616 uninstallPkg(String packageName, boolean allUsers, boolean andDisable)617 private void uninstallPkg(String packageName, boolean allUsers, boolean andDisable) { 618 stopListeningToPackageRemove(); 619 // Create new intent to launch Uninstaller activity 620 final Uri packageURI = Uri.parse("package:" + packageName); 621 final Intent uninstallIntent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageURI); 622 uninstallIntent.putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, allUsers); 623 mMetricsFeatureProvider.action( 624 getContext(), SettingsEnums.ACTION_SETTINGS_UNINSTALL_APP); 625 startActivityForResult(uninstallIntent, REQUEST_UNINSTALL); 626 } 627 startAppInfoFragment(Class<?> fragment, int title, Bundle args, SettingsPreferenceFragment caller, AppEntry appEntry)628 public static void startAppInfoFragment(Class<?> fragment, int title, Bundle args, 629 SettingsPreferenceFragment caller, AppEntry appEntry) { 630 // start new fragment to display extended information 631 if (args == null) { 632 args = new Bundle(); 633 } 634 args.putString(ARG_PACKAGE_NAME, appEntry.info.packageName); 635 args.putInt(ARG_PACKAGE_UID, appEntry.info.uid); 636 new SubSettingLauncher(caller.getContext()) 637 .setDestination(fragment.getName()) 638 .setArguments(args) 639 .setTitleRes(title) 640 .setResultListener(caller, SUB_INFO_FRAGMENT) 641 .setSourceMetricsCategory(caller.getMetricsCategory()) 642 .launch(); 643 } 644 645 /** Starts app info fragment from SPA pages. */ startAppInfoFragment( Class<?> destination, ApplicationInfo app, Context context, int sourceMetricsCategory)646 public static void startAppInfoFragment( 647 Class<?> destination, ApplicationInfo app, Context context, int sourceMetricsCategory) { 648 // start new fragment to display extended information 649 Bundle args = new Bundle(); 650 args.putString(ARG_PACKAGE_NAME, app.packageName); 651 args.putInt(ARG_PACKAGE_UID, app.uid); 652 new SubSettingLauncher(context) 653 .setDestination(destination.getName()) 654 .setArguments(args) 655 .setUserHandle(UserHandle.getUserHandleForUid(app.uid)) 656 .setSourceMetricsCategory(sourceMetricsCategory) 657 .launch(); 658 } 659 onPackageRemoved()660 private void onPackageRemoved() { 661 getActivity().finishActivity(SUB_INFO_FRAGMENT); 662 getActivity().finishAndRemoveTask(); 663 } 664 665 @VisibleForTesting getNumberOfUserWithPackageInstalled(String packageName)666 int getNumberOfUserWithPackageInstalled(String packageName) { 667 final List<UserInfo> userInfos = mUserManager.getAliveUsers(); 668 int count = 0; 669 670 for (final UserInfo userInfo : userInfos) { 671 try { 672 // Use this API to check whether user has this package 673 final ApplicationInfo info = mPm.getApplicationInfoAsUser( 674 packageName, PackageManager.GET_META_DATA, userInfo.id); 675 if ((info.flags & ApplicationInfo.FLAG_INSTALLED) != 0) { 676 count++; 677 } 678 } catch (NameNotFoundException e) { 679 Log.e(TAG, "Package: " + packageName + " not found for user: " + userInfo.id); 680 } 681 } 682 683 return count; 684 } 685 getPackageName()686 private String getPackageName() { 687 if (mPackageName != null) { 688 return mPackageName; 689 } 690 final Bundle args = getArguments(); 691 mPackageName = (args != null) ? args.getString(ARG_PACKAGE_NAME) : null; 692 if (mPackageName == null) { 693 final Intent intent = args == null ? 694 getActivity().getIntent() : (Intent) args.getParcelable("intent"); 695 if (intent != null) { 696 mPackageName = intent.getData().getSchemeSpecificPart(); 697 } 698 } 699 return mPackageName; 700 } 701 getUid()702 private int getUid() { 703 if (mUid > 0) { 704 return mUid; 705 } 706 final Bundle args = getArguments(); 707 mUid = (args != null) ? args.getInt(ARG_PACKAGE_UID) : -1; 708 if (mUid <= 0) { 709 final Intent intent = args == null 710 ? getActivity().getIntent() : (Intent) args.getParcelable("intent"); 711 mUid = intent != null && intent.getExtras() != null 712 ? mUid = intent.getIntExtra("uId", -1) : -1; 713 } 714 return mUid; 715 } 716 717 @VisibleForTesting retrieveAppEntry()718 void retrieveAppEntry() { 719 final Activity activity = getActivity(); 720 if (activity == null || mFinishing) { 721 return; 722 } 723 if (mState == null) { 724 mState = ApplicationsState.getInstance(activity.getApplication()); 725 mSession = mState.newSession(this, getSettingsLifecycle()); 726 } 727 mUserId = UserHandle.myUserId(); 728 mAppEntry = mState.getEntry(getPackageName(), UserHandle.myUserId()); 729 if (mAppEntry != null) { 730 // Get application info again to refresh changed properties of application 731 try { 732 mPackageInfo = activity.getPackageManager().getPackageInfo( 733 mAppEntry.info.packageName, 734 PackageManager.MATCH_DISABLED_COMPONENTS | 735 PackageManager.MATCH_ANY_USER | 736 PackageManager.GET_SIGNATURES | 737 PackageManager.GET_PERMISSIONS); 738 } catch (NameNotFoundException e) { 739 Log.e(TAG, "Exception when retrieving package:" + mAppEntry.info.packageName, e); 740 } 741 } else { 742 Log.w(TAG, "Missing AppEntry; maybe reinstalling?"); 743 mPackageInfo = null; 744 } 745 } 746 setIntentAndFinish(boolean finish, boolean appChanged)747 private void setIntentAndFinish(boolean finish, boolean appChanged) { 748 if (localLOGV) Log.i(TAG, "appChanged=" + appChanged); 749 final Intent intent = new Intent(); 750 intent.putExtra(ManageApplications.APP_CHG, appChanged); 751 final SettingsActivity sa = (SettingsActivity) getActivity(); 752 sa.finishPreferencePanel(Activity.RESULT_OK, intent); 753 mFinishing = true; 754 } 755 756 @Override onRunningStateChanged(boolean running)757 public void onRunningStateChanged(boolean running) { 758 // No op. 759 } 760 761 @Override onRebuildComplete(ArrayList<AppEntry> apps)762 public void onRebuildComplete(ArrayList<AppEntry> apps) { 763 // No op. 764 } 765 766 @Override onPackageIconChanged()767 public void onPackageIconChanged() { 768 // No op. 769 } 770 771 @Override onAllSizesComputed()772 public void onAllSizesComputed() { 773 // No op. 774 } 775 776 @Override onLauncherInfoChanged()777 public void onLauncherInfoChanged() { 778 // No op. 779 } 780 781 @Override onLoadEntriesCompleted()782 public void onLoadEntriesCompleted() { 783 // No op. 784 } 785 786 @Override onPackageListChanged()787 public void onPackageListChanged() { 788 if (!refreshUi()) { 789 setIntentAndFinish(true, true); 790 } 791 } 792 793 @VisibleForTesting startListeningToPackageRemove()794 void startListeningToPackageRemove() { 795 if (mListeningToPackageRemove) { 796 return; 797 } 798 mListeningToPackageRemove = true; 799 final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_REMOVED); 800 filter.addDataScheme("package"); 801 getContext().registerReceiver(mPackageRemovedReceiver, filter); 802 } 803 stopListeningToPackageRemove()804 private void stopListeningToPackageRemove() { 805 if (!mListeningToPackageRemove) { 806 return; 807 } 808 mListeningToPackageRemove = false; 809 getContext().unregisterReceiver(mPackageRemovedReceiver); 810 } 811 812 @VisibleForTesting 813 final BroadcastReceiver mPackageRemovedReceiver = new BroadcastReceiver() { 814 @Override 815 public void onReceive(Context context, Intent intent) { 816 if (mFinishing) { 817 return; 818 } 819 820 final String packageName = intent.getData().getSchemeSpecificPart(); 821 if (mAppEntry == null 822 || mAppEntry.info == null 823 || TextUtils.equals(mAppEntry.info.packageName, packageName)) { 824 onPackageRemoved(); 825 } else if (mAppEntry.info.isResourceOverlay() 826 && TextUtils.equals(mPackageInfo.overlayTarget, packageName)) { 827 refreshUi(); 828 } 829 } 830 }; 831 832 } 833