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 use(AdvancedAppInfoPreferenceCategoryController.class).setChildren(Arrays.asList( 219 writeSystemSettings, drawOverlay, pip, externalSource, acrossProfiles, 220 alarmsAndReminders)); 221 222 final AppLocalePreferenceController appLocale = 223 use(AppLocalePreferenceController.class); 224 appLocale.setParentFragment(this); 225 } 226 227 @Override onCreate(Bundle icicle)228 public void onCreate(Bundle icicle) { 229 super.onCreate(icicle); 230 mFinishing = false; 231 final Activity activity = getActivity(); 232 mDpm = (DevicePolicyManager) activity.getSystemService(Context.DEVICE_POLICY_SERVICE); 233 mUserManager = (UserManager) activity.getSystemService(Context.USER_SERVICE); 234 mPm = activity.getPackageManager(); 235 if (!ensurePackageInfoAvailable(activity)) { 236 return; 237 } 238 if (!ensureDisplayableModule(activity)) { 239 return; 240 } 241 startListeningToPackageRemove(); 242 243 setHasOptionsMenu(true); 244 replaceEnterpriseStringTitle("interact_across_profiles", 245 CONNECTED_WORK_AND_PERSONAL_APPS_TITLE, R.string.interact_across_profiles_title); 246 } 247 248 @Override onCreatePreferences(Bundle savedInstanceState, String rootKey)249 public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { 250 if (!ensurePackageInfoAvailable(getActivity())) { 251 return; 252 } 253 super.onCreatePreferences(savedInstanceState, rootKey); 254 } 255 256 @Override onDestroy()257 public void onDestroy() { 258 stopListeningToPackageRemove(); 259 super.onDestroy(); 260 } 261 262 @Override getMetricsCategory()263 public int getMetricsCategory() { 264 return SettingsEnums.APPLICATIONS_INSTALLED_APP_DETAILS; 265 } 266 267 @Override onResume()268 public void onResume() { 269 super.onResume(); 270 final Activity activity = getActivity(); 271 mAppsControlDisallowedAdmin = RestrictedLockUtilsInternal.checkIfRestrictionEnforced( 272 activity, UserManager.DISALLOW_APPS_CONTROL, mUserId); 273 mAppsControlDisallowedBySystem = RestrictedLockUtilsInternal.hasBaseUserRestriction( 274 activity, UserManager.DISALLOW_APPS_CONTROL, mUserId); 275 276 if (!refreshUi()) { 277 setIntentAndFinish(true, true); 278 } 279 getActivity().invalidateOptionsMenu(); 280 } 281 282 @Override getPreferenceScreenResId()283 protected int getPreferenceScreenResId() { 284 return R.xml.app_info_settings; 285 } 286 287 @Override getLogTag()288 protected String getLogTag() { 289 return TAG; 290 } 291 292 @Override createPreferenceControllers(Context context)293 protected List<AbstractPreferenceController> createPreferenceControllers(Context context) { 294 retrieveAppEntry(); 295 if (mPackageInfo == null) { 296 return null; 297 } 298 final String packageName = getPackageName(); 299 final List<AbstractPreferenceController> controllers = new ArrayList<>(); 300 final Lifecycle lifecycle = getSettingsLifecycle(); 301 302 // The following are controllers for preferences that needs to refresh the preference state 303 // when app state changes. 304 controllers.add( 305 new AppHeaderViewPreferenceController(context, this, packageName, lifecycle)); 306 307 for (AbstractPreferenceController controller : controllers) { 308 mCallbacks.add((Callback) controller); 309 } 310 311 // The following are controllers for preferences that don't need to refresh the preference 312 // state when app state changes. 313 mInstantAppButtonPreferenceController = 314 new InstantAppButtonsPreferenceController(context, this, packageName, lifecycle); 315 controllers.add(mInstantAppButtonPreferenceController); 316 mAppButtonsPreferenceController = new AppButtonsPreferenceController( 317 (SettingsActivity) getActivity(), this, lifecycle, packageName, mState, 318 REQUEST_UNINSTALL, REQUEST_REMOVE_DEVICE_ADMIN); 319 controllers.add(mAppButtonsPreferenceController); 320 controllers.add(new AppBatteryPreferenceController( 321 context, this, packageName, getUid(), lifecycle)); 322 controllers.add(new AppMemoryPreferenceController(context, this, lifecycle)); 323 controllers.add(new DefaultHomeShortcutPreferenceController(context, packageName)); 324 controllers.add(new DefaultBrowserShortcutPreferenceController(context, packageName)); 325 controllers.add(new DefaultPhoneShortcutPreferenceController(context, packageName)); 326 controllers.add(new DefaultEmergencyShortcutPreferenceController(context, packageName)); 327 controllers.add(new DefaultSmsShortcutPreferenceController(context, packageName)); 328 329 return controllers; 330 } 331 addToCallbackList(Callback callback)332 void addToCallbackList(Callback callback) { 333 if (callback != null) { 334 mCallbacks.add(callback); 335 } 336 } 337 getAppEntry()338 ApplicationsState.AppEntry getAppEntry() { 339 return mAppEntry; 340 } 341 setAppEntry(ApplicationsState.AppEntry appEntry)342 void setAppEntry(ApplicationsState.AppEntry appEntry) { 343 mAppEntry = appEntry; 344 } 345 getPackageInfo()346 public PackageInfo getPackageInfo() { 347 return mPackageInfo; 348 } 349 350 @Override onPackageSizeChanged(String packageName)351 public void onPackageSizeChanged(String packageName) { 352 if (!TextUtils.equals(packageName, mPackageName)) { 353 Log.d(TAG, "Package change irrelevant, skipping"); 354 return; 355 } 356 refreshUi(); 357 } 358 359 /** 360 * Ensures the {@link PackageInfo} is available to proceed. If it's not available, the fragment 361 * will finish. 362 * 363 * @return true if packageInfo is available. 364 */ 365 @VisibleForTesting ensurePackageInfoAvailable(Activity activity)366 boolean ensurePackageInfoAvailable(Activity activity) { 367 if (mPackageInfo == null) { 368 mFinishing = true; 369 Log.w(TAG, "Package info not available. Is this package already uninstalled?"); 370 activity.finishAndRemoveTask(); 371 return false; 372 } 373 return true; 374 } 375 376 /** 377 * Ensures the package is displayable as directed by {@link AppUtils#isHiddenSystemModule}. 378 * If it's not, the fragment will finish. 379 * 380 * @return true if package is displayable. 381 */ 382 @VisibleForTesting ensureDisplayableModule(Activity activity)383 boolean ensureDisplayableModule(Activity activity) { 384 if (AppUtils.isHiddenSystemModule(activity.getApplicationContext(), mPackageName)) { 385 mFinishing = true; 386 Log.w(TAG, "Package is hidden module, exiting: " + mPackageName); 387 activity.finishAndRemoveTask(); 388 return false; 389 } 390 return true; 391 } 392 393 @Override onCreateOptionsMenu(Menu menu, MenuInflater inflater)394 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 395 super.onCreateOptionsMenu(menu, inflater); 396 menu.add(0, UNINSTALL_UPDATES, 0, R.string.app_factory_reset) 397 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); 398 menu.add(0, UNINSTALL_ALL_USERS_MENU, 1, R.string.uninstall_all_users_text) 399 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); 400 menu.add(0, ACCESS_RESTRICTED_SETTINGS, 0, 401 R.string.app_restricted_settings_lockscreen_title) 402 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); 403 } 404 405 @Override onPrepareOptionsMenu(Menu menu)406 public void onPrepareOptionsMenu(Menu menu) { 407 if (mFinishing) { 408 return; 409 } 410 super.onPrepareOptionsMenu(menu); 411 final MenuItem uninstallAllUsersItem = menu.findItem(UNINSTALL_ALL_USERS_MENU); 412 uninstallAllUsersItem.setVisible( 413 shouldShowUninstallForAll(mAppEntry) && !mAppsControlDisallowedBySystem); 414 if (uninstallAllUsersItem.isVisible()) { 415 RestrictedLockUtilsInternal.setMenuItemAsDisabledByAdmin(getActivity(), 416 uninstallAllUsersItem, mAppsControlDisallowedAdmin); 417 } 418 menu.findItem(ACCESS_RESTRICTED_SETTINGS).setVisible(shouldShowAccessRestrictedSettings()); 419 mUpdatedSysApp = (mAppEntry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0; 420 final MenuItem uninstallUpdatesItem = menu.findItem(UNINSTALL_UPDATES); 421 final boolean uninstallUpdateDisabled = getContext().getResources().getBoolean( 422 R.bool.config_disable_uninstall_update); 423 uninstallUpdatesItem.setVisible(mUserManager.isAdminUser() 424 && mUpdatedSysApp 425 && !mAppsControlDisallowedBySystem 426 && !uninstallUpdateDisabled); 427 if (uninstallUpdatesItem.isVisible()) { 428 RestrictedLockUtilsInternal.setMenuItemAsDisabledByAdmin(getActivity(), 429 uninstallUpdatesItem, mAppsControlDisallowedAdmin); 430 } 431 } 432 showLockScreen(Context context, Runnable successRunnable)433 private static void showLockScreen(Context context, Runnable successRunnable) { 434 final KeyguardManager keyguardManager = context.getSystemService( 435 KeyguardManager.class); 436 437 if (keyguardManager.isKeyguardSecure()) { 438 final BiometricPrompt.AuthenticationCallback authenticationCallback = 439 new BiometricPrompt.AuthenticationCallback() { 440 @Override 441 public void onAuthenticationSucceeded( 442 BiometricPrompt.AuthenticationResult result) { 443 successRunnable.run(); 444 } 445 446 @Override 447 public void onAuthenticationError(int errorCode, CharSequence errString) { 448 //Do nothing 449 } 450 }; 451 452 final BiometricPrompt.Builder builder = new BiometricPrompt.Builder(context) 453 .setUseDefaultTitle(); // use default title if title is null/empty 454 455 final BiometricManager bm = context.getSystemService(BiometricManager.class); 456 final int authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL 457 | BiometricManager.Authenticators.BIOMETRIC_WEAK; 458 if (bm.canAuthenticate(authenticators) == BiometricManager.BIOMETRIC_SUCCESS) { 459 builder.setAllowedAuthenticators(authenticators); 460 builder.setSubtitle(bm.getStrings(authenticators).getPromptMessage()); 461 } 462 463 final BiometricPrompt bp = builder.build(); 464 final Handler handler = new Handler(Looper.getMainLooper()); 465 bp.authenticate(new CancellationSignal(), 466 runnable -> handler.post(runnable), 467 authenticationCallback); 468 } else { 469 successRunnable.run(); 470 } 471 } 472 473 @Override onOptionsItemSelected(MenuItem item)474 public boolean onOptionsItemSelected(MenuItem item) { 475 switch (item.getItemId()) { 476 case UNINSTALL_ALL_USERS_MENU: 477 uninstallPkg(mAppEntry.info.packageName, true, false); 478 return true; 479 case UNINSTALL_UPDATES: 480 uninstallPkg(mAppEntry.info.packageName, false, false); 481 return true; 482 case ACCESS_RESTRICTED_SETTINGS: 483 showLockScreen(getContext(), () -> { 484 final AppOpsManager appOpsManager = getContext().getSystemService( 485 AppOpsManager.class); 486 appOpsManager.setMode(AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS, 487 getUid(), 488 getPackageName(), 489 AppOpsManager.MODE_ALLOWED); 490 getActivity().invalidateOptionsMenu(); 491 final String toastString = getContext().getString( 492 R.string.toast_allows_restricted_settings_successfully, 493 mAppEntry.label); 494 Toast.makeText(getContext(), toastString, Toast.LENGTH_LONG).show(); 495 }); 496 return true; 497 } 498 return super.onOptionsItemSelected(item); 499 } 500 501 @Override onActivityResult(int requestCode, int resultCode, Intent data)502 public void onActivityResult(int requestCode, int resultCode, Intent data) { 503 super.onActivityResult(requestCode, resultCode, data); 504 if (requestCode == REQUEST_UNINSTALL) { 505 // Refresh option menu 506 getActivity().invalidateOptionsMenu(); 507 } 508 if (mAppButtonsPreferenceController != null) { 509 mAppButtonsPreferenceController.handleActivityResult(requestCode, resultCode, data); 510 } 511 } 512 513 @Override handleDialogClick(int id)514 public void handleDialogClick(int id) { 515 if (mAppButtonsPreferenceController != null) { 516 mAppButtonsPreferenceController.handleDialogClick(id); 517 } 518 } 519 shouldShowAccessRestrictedSettings()520 private boolean shouldShowAccessRestrictedSettings() { 521 try { 522 final int mode = getSystemService(AppOpsManager.class).noteOpNoThrow( 523 AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS, getUid(), 524 getPackageName()); 525 return mode == AppOpsManager.MODE_IGNORED; 526 } catch (Exception e) { 527 // Fallback in case if app ops is not available in testing. 528 return false; 529 } 530 } 531 532 @VisibleForTesting shouldShowUninstallForAll(AppEntry appEntry)533 boolean shouldShowUninstallForAll(AppEntry appEntry) { 534 boolean showIt = true; 535 if (mUpdatedSysApp) { 536 showIt = false; 537 } else if (appEntry == null) { 538 showIt = false; 539 } else if ((appEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { 540 showIt = false; 541 } else if (mPackageInfo == null || mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) { 542 showIt = false; 543 } else if (UserHandle.myUserId() != 0) { 544 showIt = false; 545 } else if (mUserManager.getUsers().size() < 2) { 546 showIt = false; 547 } else if (getNumberOfUserWithPackageInstalled(mPackageName) < 2 548 && (appEntry.info.flags & ApplicationInfo.FLAG_INSTALLED) != 0) { 549 showIt = false; 550 } else if (AppUtils.isInstant(appEntry.info)) { 551 showIt = false; 552 } 553 return showIt; 554 } 555 556 @VisibleForTesting refreshUi()557 boolean refreshUi() { 558 retrieveAppEntry(); 559 if (mAppEntry == null) { 560 return false; // onCreate must have failed, make sure to exit 561 } 562 563 if (mPackageInfo == null) { 564 return false; // onCreate must have failed, make sure to exit 565 } 566 567 mState.ensureIcon(mAppEntry); 568 569 // Update the preference summaries. 570 for (Callback callback : mCallbacks) { 571 callback.refreshUi(); 572 } 573 if (mAppButtonsPreferenceController.isAvailable()) { 574 mAppButtonsPreferenceController.refreshUi(); 575 } 576 577 if (!mInitialized) { 578 // First time init: are we displaying an uninstalled app? 579 mInitialized = true; 580 mShowUninstalled = (mAppEntry.info.flags & ApplicationInfo.FLAG_INSTALLED) == 0; 581 } else { 582 // All other times: if the app no longer exists then we want 583 // to go away. 584 try { 585 final ApplicationInfo ainfo = getActivity().getPackageManager().getApplicationInfo( 586 mAppEntry.info.packageName, 587 PackageManager.MATCH_DISABLED_COMPONENTS 588 | PackageManager.MATCH_ANY_USER); 589 if (!mShowUninstalled) { 590 // If we did not start out with the app uninstalled, then 591 // it transitioning to the uninstalled state for the current 592 // user means we should go away as well. 593 return (ainfo.flags & ApplicationInfo.FLAG_INSTALLED) != 0; 594 } 595 } catch (NameNotFoundException e) { 596 return false; 597 } 598 } 599 600 return true; 601 } 602 603 @Override shouldSkipForInitialSUW()604 protected boolean shouldSkipForInitialSUW() { 605 return true; 606 } 607 uninstallPkg(String packageName, boolean allUsers, boolean andDisable)608 private void uninstallPkg(String packageName, boolean allUsers, boolean andDisable) { 609 stopListeningToPackageRemove(); 610 // Create new intent to launch Uninstaller activity 611 final Uri packageURI = Uri.parse("package:" + packageName); 612 final Intent uninstallIntent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageURI); 613 uninstallIntent.putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, allUsers); 614 mMetricsFeatureProvider.action( 615 getContext(), SettingsEnums.ACTION_SETTINGS_UNINSTALL_APP); 616 startActivityForResult(uninstallIntent, REQUEST_UNINSTALL); 617 } 618 startAppInfoFragment(Class<?> fragment, int title, Bundle args, SettingsPreferenceFragment caller, AppEntry appEntry)619 public static void startAppInfoFragment(Class<?> fragment, int title, Bundle args, 620 SettingsPreferenceFragment caller, AppEntry appEntry) { 621 // start new fragment to display extended information 622 if (args == null) { 623 args = new Bundle(); 624 } 625 args.putString(ARG_PACKAGE_NAME, appEntry.info.packageName); 626 args.putInt(ARG_PACKAGE_UID, appEntry.info.uid); 627 new SubSettingLauncher(caller.getContext()) 628 .setDestination(fragment.getName()) 629 .setArguments(args) 630 .setTitleRes(title) 631 .setResultListener(caller, SUB_INFO_FRAGMENT) 632 .setSourceMetricsCategory(caller.getMetricsCategory()) 633 .launch(); 634 } 635 onPackageRemoved()636 private void onPackageRemoved() { 637 getActivity().finishActivity(SUB_INFO_FRAGMENT); 638 getActivity().finishAndRemoveTask(); 639 } 640 641 @VisibleForTesting getNumberOfUserWithPackageInstalled(String packageName)642 int getNumberOfUserWithPackageInstalled(String packageName) { 643 final List<UserInfo> userInfos = mUserManager.getAliveUsers(); 644 int count = 0; 645 646 for (final UserInfo userInfo : userInfos) { 647 try { 648 // Use this API to check whether user has this package 649 final ApplicationInfo info = mPm.getApplicationInfoAsUser( 650 packageName, PackageManager.GET_META_DATA, userInfo.id); 651 if ((info.flags & ApplicationInfo.FLAG_INSTALLED) != 0) { 652 count++; 653 } 654 } catch (NameNotFoundException e) { 655 Log.e(TAG, "Package: " + packageName + " not found for user: " + userInfo.id); 656 } 657 } 658 659 return count; 660 } 661 getPackageName()662 private String getPackageName() { 663 if (mPackageName != null) { 664 return mPackageName; 665 } 666 final Bundle args = getArguments(); 667 mPackageName = (args != null) ? args.getString(ARG_PACKAGE_NAME) : null; 668 if (mPackageName == null) { 669 final Intent intent = args == null ? 670 getActivity().getIntent() : (Intent) args.getParcelable("intent"); 671 if (intent != null) { 672 mPackageName = intent.getData().getSchemeSpecificPart(); 673 } 674 } 675 return mPackageName; 676 } 677 getUid()678 private int getUid() { 679 if (mUid > 0) { 680 return mUid; 681 } 682 final Bundle args = getArguments(); 683 mUid = (args != null) ? args.getInt(ARG_PACKAGE_UID) : -1; 684 if (mUid <= 0) { 685 final Intent intent = args == null 686 ? getActivity().getIntent() : (Intent) args.getParcelable("intent"); 687 mUid = intent != null && intent.getExtras() != null 688 ? mUid = intent.getIntExtra("uId", -1) : -1; 689 } 690 return mUid; 691 } 692 693 @VisibleForTesting retrieveAppEntry()694 void retrieveAppEntry() { 695 final Activity activity = getActivity(); 696 if (activity == null || mFinishing) { 697 return; 698 } 699 if (mState == null) { 700 mState = ApplicationsState.getInstance(activity.getApplication()); 701 mSession = mState.newSession(this, getSettingsLifecycle()); 702 } 703 mUserId = UserHandle.myUserId(); 704 mAppEntry = mState.getEntry(getPackageName(), UserHandle.myUserId()); 705 if (mAppEntry != null) { 706 // Get application info again to refresh changed properties of application 707 try { 708 mPackageInfo = activity.getPackageManager().getPackageInfo( 709 mAppEntry.info.packageName, 710 PackageManager.MATCH_DISABLED_COMPONENTS | 711 PackageManager.MATCH_ANY_USER | 712 PackageManager.GET_SIGNATURES | 713 PackageManager.GET_PERMISSIONS); 714 } catch (NameNotFoundException e) { 715 Log.e(TAG, "Exception when retrieving package:" + mAppEntry.info.packageName, e); 716 } 717 } else { 718 Log.w(TAG, "Missing AppEntry; maybe reinstalling?"); 719 mPackageInfo = null; 720 } 721 } 722 setIntentAndFinish(boolean finish, boolean appChanged)723 private void setIntentAndFinish(boolean finish, boolean appChanged) { 724 if (localLOGV) Log.i(TAG, "appChanged=" + appChanged); 725 final Intent intent = new Intent(); 726 intent.putExtra(ManageApplications.APP_CHG, appChanged); 727 final SettingsActivity sa = (SettingsActivity) getActivity(); 728 sa.finishPreferencePanel(Activity.RESULT_OK, intent); 729 mFinishing = true; 730 } 731 732 @Override onRunningStateChanged(boolean running)733 public void onRunningStateChanged(boolean running) { 734 // No op. 735 } 736 737 @Override onRebuildComplete(ArrayList<AppEntry> apps)738 public void onRebuildComplete(ArrayList<AppEntry> apps) { 739 // No op. 740 } 741 742 @Override onPackageIconChanged()743 public void onPackageIconChanged() { 744 // No op. 745 } 746 747 @Override onAllSizesComputed()748 public void onAllSizesComputed() { 749 // No op. 750 } 751 752 @Override onLauncherInfoChanged()753 public void onLauncherInfoChanged() { 754 // No op. 755 } 756 757 @Override onLoadEntriesCompleted()758 public void onLoadEntriesCompleted() { 759 // No op. 760 } 761 762 @Override onPackageListChanged()763 public void onPackageListChanged() { 764 if (!refreshUi()) { 765 setIntentAndFinish(true, true); 766 } 767 } 768 769 @VisibleForTesting startListeningToPackageRemove()770 void startListeningToPackageRemove() { 771 if (mListeningToPackageRemove) { 772 return; 773 } 774 mListeningToPackageRemove = true; 775 final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_REMOVED); 776 filter.addDataScheme("package"); 777 getContext().registerReceiver(mPackageRemovedReceiver, filter); 778 } 779 stopListeningToPackageRemove()780 private void stopListeningToPackageRemove() { 781 if (!mListeningToPackageRemove) { 782 return; 783 } 784 mListeningToPackageRemove = false; 785 getContext().unregisterReceiver(mPackageRemovedReceiver); 786 } 787 788 @VisibleForTesting 789 final BroadcastReceiver mPackageRemovedReceiver = new BroadcastReceiver() { 790 @Override 791 public void onReceive(Context context, Intent intent) { 792 if (mFinishing) { 793 return; 794 } 795 796 final String packageName = intent.getData().getSchemeSpecificPart(); 797 if (mAppEntry == null 798 || mAppEntry.info == null 799 || TextUtils.equals(mAppEntry.info.packageName, packageName)) { 800 onPackageRemoved(); 801 } else if (mAppEntry.info.isResourceOverlay() 802 && TextUtils.equals(mPackageInfo.overlayTarget, packageName)) { 803 refreshUi(); 804 } 805 } 806 }; 807 808 } 809