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