AppInfoDashboardFragment.java revision b5c651c9399aa53e0bef296a265a47c825fe12b2
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 17package com.android.settings.applications.appinfo; 18 19import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; 20 21import android.app.Activity; 22import android.app.ActivityManager; 23import android.app.AlertDialog; 24import android.app.Dialog; 25import android.app.DialogFragment; 26import android.app.admin.DevicePolicyManager; 27import android.content.BroadcastReceiver; 28import android.content.Context; 29import android.content.DialogInterface; 30import android.content.Intent; 31import android.content.IntentFilter; 32import android.content.pm.ApplicationInfo; 33import android.content.pm.PackageInfo; 34import android.content.pm.PackageManager; 35import android.content.pm.PackageManager.NameNotFoundException; 36import android.content.pm.UserInfo; 37import android.net.Uri; 38import android.os.AsyncTask; 39import android.os.Bundle; 40import android.os.UserHandle; 41import android.os.UserManager; 42import android.support.annotation.VisibleForTesting; 43import android.text.TextUtils; 44import android.util.Log; 45import android.view.Menu; 46import android.view.MenuInflater; 47import android.view.MenuItem; 48 49import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 50import com.android.settings.DeviceAdminAdd; 51import com.android.settings.R; 52import com.android.settings.SettingsActivity; 53import com.android.settings.SettingsPreferenceFragment; 54import com.android.settings.applications.manageapplications.ManageApplications; 55import com.android.settings.core.SubSettingLauncher; 56import com.android.settings.core.instrumentation.InstrumentedDialogFragment; 57import com.android.settings.dashboard.DashboardFragment; 58import com.android.settings.wrapper.DevicePolicyManagerWrapper; 59import com.android.settingslib.RestrictedLockUtils; 60import com.android.settingslib.applications.AppUtils; 61import com.android.settingslib.applications.ApplicationsState; 62import com.android.settingslib.applications.ApplicationsState.AppEntry; 63import com.android.settingslib.core.AbstractPreferenceController; 64import com.android.settingslib.core.lifecycle.Lifecycle; 65 66import java.lang.ref.WeakReference; 67import java.util.ArrayList; 68import java.util.Arrays; 69import java.util.List; 70 71/** 72 * Dashboard fragment to display application information from Settings. This activity presents 73 * extended information associated with a package like code, data, total size, permissions 74 * used by the application and also the set of default launchable activities. 75 * For system applications, an option to clear user data is displayed only if data size is > 0. 76 * System applications that do not want clear user data do not have this option. 77 * For non-system applications, there is no option to clear data. Instead there is an option to 78 * uninstall the application. 79 */ 80public class AppInfoDashboardFragment extends DashboardFragment 81 implements ApplicationsState.Callbacks { 82 83 private static final String TAG = "AppInfoDashboard"; 84 85 // Menu identifiers 86 @VisibleForTesting 87 static final int UNINSTALL_ALL_USERS_MENU = 1; 88 @VisibleForTesting 89 static final int UNINSTALL_UPDATES = 2; 90 static final int INSTALL_INSTANT_APP_MENU = 3; 91 92 // Result code identifiers 93 @VisibleForTesting 94 static final int REQUEST_UNINSTALL = 0; 95 private static final int REQUEST_REMOVE_DEVICE_ADMIN = 1; 96 97 static final int SUB_INFO_FRAGMENT = 1; 98 99 static final int LOADER_CHART_DATA = 2; 100 static final int LOADER_STORAGE = 3; 101 static final int LOADER_BATTERY = 4; 102 103 // Dialog identifiers used in showDialog 104 private static final int DLG_BASE = 0; 105 private static final int DLG_FORCE_STOP = DLG_BASE + 1; 106 private static final int DLG_DISABLE = DLG_BASE + 2; 107 private static final int DLG_SPECIAL_DISABLE = DLG_BASE + 3; 108 static final int DLG_CLEAR_INSTANT_APP = DLG_BASE + 4; 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 125 private DevicePolicyManagerWrapper mDpm; 126 private UserManager mUserManager; 127 private PackageManager mPm; 128 129 private boolean mFinishing; 130 private boolean mListeningToPackageRemove; 131 132 133 private boolean mInitialized; 134 private boolean mShowUninstalled; 135 private boolean mUpdatedSysApp = false; 136 private boolean mDisableAfterUninstall; 137 138 private List<Callback> mCallbacks = new ArrayList<>(); 139 140 private InstantAppButtonsPreferenceController mInstantAppButtonPreferenceController; 141 private AppActionButtonPreferenceController mAppActionButtonPreferenceController; 142 143 /** 144 * Callback to invoke when app info has been changed. 145 */ 146 public interface Callback { 147 void refreshUi(); 148 } 149 150 private boolean isDisabledUntilUsed() { 151 return mAppEntry.info.enabledSetting 152 == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED; 153 } 154 155 @Override 156 public void onAttach(Context context) { 157 super.onAttach(context); 158 final String packageName = getPackageName(); 159 use(TimeSpentInAppPreferenceController.class).setPackageName(packageName); 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 use(AppOpenByDefaultPreferenceController.class).setParentFragment(this); 169 use(AppPermissionPreferenceController.class).setParentFragment(this); 170 use(AppPermissionPreferenceController.class).setPackageName(packageName); 171 use(AppStoragePreferenceController.class).setParentFragment(this); 172 use(AppVersionPreferenceController.class).setParentFragment(this); 173 use(InstantAppDomainsPreferenceController.class).setParentFragment(this); 174 175 final WriteSystemSettingsPreferenceController writeSystemSettings = 176 use(WriteSystemSettingsPreferenceController.class); 177 writeSystemSettings.setParentFragment(this); 178 179 final DrawOverlayDetailPreferenceController drawOverlay = 180 use(DrawOverlayDetailPreferenceController.class); 181 drawOverlay.setParentFragment(this); 182 183 final PictureInPictureDetailPreferenceController pip = 184 use(PictureInPictureDetailPreferenceController.class); 185 pip.setPackageName(packageName); 186 pip.setParentFragment(this); 187 final ExternalSourceDetailPreferenceController externalSource = 188 use(ExternalSourceDetailPreferenceController.class); 189 externalSource.setPackageName(packageName); 190 externalSource.setParentFragment(this); 191 192 use(AdvancedAppInfoPreferenceCategoryController.class).setChildren(Arrays.asList( 193 writeSystemSettings, drawOverlay, pip, externalSource)); 194 } 195 196 @Override 197 public void onCreate(Bundle icicle) { 198 super.onCreate(icicle); 199 mFinishing = false; 200 final Activity activity = getActivity(); 201 mDpm = new DevicePolicyManagerWrapper( 202 (DevicePolicyManager) activity.getSystemService(Context.DEVICE_POLICY_SERVICE)); 203 mUserManager = (UserManager) activity.getSystemService(Context.USER_SERVICE); 204 mPm = activity.getPackageManager(); 205 206 if (!ensurePackageInfoAvailable(activity)) { 207 return; 208 } 209 210 startListeningToPackageRemove(); 211 212 setHasOptionsMenu(true); 213 } 214 215 @Override 216 public void onDestroy() { 217 stopListeningToPackageRemove(); 218 super.onDestroy(); 219 } 220 221 @Override 222 public int getMetricsCategory() { 223 return MetricsEvent.APPLICATIONS_INSTALLED_APP_DETAILS; 224 } 225 226 @Override 227 public void onResume() { 228 super.onResume(); 229 final Activity activity = getActivity(); 230 mAppsControlDisallowedAdmin = RestrictedLockUtils.checkIfRestrictionEnforced(activity, 231 UserManager.DISALLOW_APPS_CONTROL, mUserId); 232 mAppsControlDisallowedBySystem = RestrictedLockUtils.hasBaseUserRestriction(activity, 233 UserManager.DISALLOW_APPS_CONTROL, mUserId); 234 235 if (!refreshUi()) { 236 setIntentAndFinish(true, true); 237 } 238 } 239 240 @Override 241 protected int getPreferenceScreenResId() { 242 return R.xml.app_info_settings; 243 } 244 245 @Override 246 protected String getLogTag() { 247 return TAG; 248 } 249 250 @Override 251 protected List<AbstractPreferenceController> createPreferenceControllers(Context context) { 252 retrieveAppEntry(); 253 if (mPackageInfo == null) { 254 return null; 255 } 256 final String packageName = getPackageName(); 257 final List<AbstractPreferenceController> controllers = new ArrayList<>(); 258 final Lifecycle lifecycle = getLifecycle(); 259 260 // The following are controllers for preferences that needs to refresh the preference state 261 // when app state changes. 262 controllers.add( 263 new AppHeaderViewPreferenceController(context, this, packageName, lifecycle)); 264 mAppActionButtonPreferenceController = 265 new AppActionButtonPreferenceController(context, this, packageName); 266 controllers.add(mAppActionButtonPreferenceController); 267 268 for (AbstractPreferenceController controller : controllers) { 269 mCallbacks.add((Callback) controller); 270 } 271 272 // The following are controllers for preferences that don't need to refresh the preference 273 // state when app state changes. 274 mInstantAppButtonPreferenceController = 275 new InstantAppButtonsPreferenceController(context, this, packageName, lifecycle); 276 controllers.add(mInstantAppButtonPreferenceController); 277 controllers.add(new AppBatteryPreferenceController(context, this, packageName, lifecycle)); 278 controllers.add(new AppMemoryPreferenceController(context, this, lifecycle)); 279 controllers.add(new DefaultHomeShortcutPreferenceController(context, packageName)); 280 controllers.add(new DefaultBrowserShortcutPreferenceController(context, packageName)); 281 controllers.add(new DefaultPhoneShortcutPreferenceController(context, packageName)); 282 controllers.add(new DefaultEmergencyShortcutPreferenceController(context, packageName)); 283 controllers.add(new DefaultSmsShortcutPreferenceController(context, packageName)); 284 285 return controllers; 286 } 287 288 void addToCallbackList(Callback callback) { 289 if (callback != null) { 290 mCallbacks.add(callback); 291 } 292 } 293 294 ApplicationsState.AppEntry getAppEntry() { 295 return mAppEntry; 296 } 297 298 void setAppEntry(ApplicationsState.AppEntry appEntry) { 299 mAppEntry = appEntry; 300 } 301 302 PackageInfo getPackageInfo() { 303 return mPackageInfo; 304 } 305 306 @Override 307 public void onPackageSizeChanged(String packageName) { 308 if (!TextUtils.equals(packageName, mPackageName)) { 309 Log.d(TAG, "Package change irrelevant, skipping"); 310 return; 311 } 312 refreshUi(); 313 } 314 315 /** 316 * Ensures the {@link PackageInfo} is available to proceed. If it's not available, the fragment 317 * will finish. 318 * 319 * @return true if packageInfo is available. 320 */ 321 @VisibleForTesting 322 boolean ensurePackageInfoAvailable(Activity activity) { 323 if (mPackageInfo == null) { 324 mFinishing = true; 325 Log.w(TAG, "Package info not available. Is this package already uninstalled?"); 326 activity.finishAndRemoveTask(); 327 return false; 328 } 329 return true; 330 } 331 332 @Override 333 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 334 super.onCreateOptionsMenu(menu, inflater); 335 menu.add(0, UNINSTALL_UPDATES, 0, R.string.app_factory_reset) 336 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); 337 menu.add(0, UNINSTALL_ALL_USERS_MENU, 1, R.string.uninstall_all_users_text) 338 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); 339 } 340 341 @Override 342 public void onPrepareOptionsMenu(Menu menu) { 343 if (mFinishing) { 344 return; 345 } 346 super.onPrepareOptionsMenu(menu); 347 menu.findItem(UNINSTALL_ALL_USERS_MENU).setVisible(shouldShowUninstallForAll(mAppEntry)); 348 mUpdatedSysApp = (mAppEntry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0; 349 final MenuItem uninstallUpdatesItem = menu.findItem(UNINSTALL_UPDATES); 350 final boolean uninstallUpdateDisabled = getContext().getResources().getBoolean( 351 R.bool.config_disable_uninstall_update); 352 uninstallUpdatesItem.setVisible( 353 mUpdatedSysApp && !mAppsControlDisallowedBySystem && !uninstallUpdateDisabled); 354 if (uninstallUpdatesItem.isVisible()) { 355 RestrictedLockUtils.setMenuItemAsDisabledByAdmin(getActivity(), 356 uninstallUpdatesItem, mAppsControlDisallowedAdmin); 357 } 358 } 359 360 @Override 361 public boolean onOptionsItemSelected(MenuItem item) { 362 switch (item.getItemId()) { 363 case UNINSTALL_ALL_USERS_MENU: 364 uninstallPkg(mAppEntry.info.packageName, true, false); 365 return true; 366 case UNINSTALL_UPDATES: 367 uninstallPkg(mAppEntry.info.packageName, false, false); 368 return true; 369 } 370 return super.onOptionsItemSelected(item); 371 } 372 373 @Override 374 public void onActivityResult(int requestCode, int resultCode, Intent data) { 375 super.onActivityResult(requestCode, resultCode, data); 376 switch (requestCode) { 377 case REQUEST_UNINSTALL: 378 // Refresh option menu 379 getActivity().invalidateOptionsMenu(); 380 381 if (mDisableAfterUninstall) { 382 mDisableAfterUninstall = false; 383 new DisableChanger(this, mAppEntry.info, 384 PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) 385 .execute((Object) null); 386 } 387 if (!refreshUi()) { 388 onPackageRemoved(); 389 } else { 390 startListeningToPackageRemove(); 391 } 392 break; 393 case REQUEST_REMOVE_DEVICE_ADMIN: 394 if (!refreshUi()) { 395 setIntentAndFinish(true, true); 396 } else { 397 startListeningToPackageRemove(); 398 } 399 break; 400 } 401 } 402 403 @VisibleForTesting 404 boolean shouldShowUninstallForAll(AppEntry appEntry) { 405 boolean showIt = true; 406 if (mUpdatedSysApp) { 407 showIt = false; 408 } else if (appEntry == null) { 409 showIt = false; 410 } else if ((appEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { 411 showIt = false; 412 } else if (mPackageInfo == null || mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) { 413 showIt = false; 414 } else if (UserHandle.myUserId() != 0) { 415 showIt = false; 416 } else if (mUserManager.getUsers().size() < 2) { 417 showIt = false; 418 } else if (getNumberOfUserWithPackageInstalled(mPackageName) < 2 419 && (appEntry.info.flags & ApplicationInfo.FLAG_INSTALLED) != 0) { 420 showIt = false; 421 } else if (AppUtils.isInstant(appEntry.info)) { 422 showIt = false; 423 } 424 return showIt; 425 } 426 427 @VisibleForTesting 428 boolean refreshUi() { 429 retrieveAppEntry(); 430 if (mAppEntry == null) { 431 return false; // onCreate must have failed, make sure to exit 432 } 433 434 if (mPackageInfo == null) { 435 return false; // onCreate must have failed, make sure to exit 436 } 437 438 mState.ensureIcon(mAppEntry); 439 440 // Update the preference summaries. 441 for (Callback callback : mCallbacks) { 442 callback.refreshUi(); 443 } 444 445 if (!mInitialized) { 446 // First time init: are we displaying an uninstalled app? 447 mInitialized = true; 448 mShowUninstalled = (mAppEntry.info.flags & ApplicationInfo.FLAG_INSTALLED) == 0; 449 } else { 450 // All other times: if the app no longer exists then we want 451 // to go away. 452 try { 453 final ApplicationInfo ainfo = getActivity().getPackageManager().getApplicationInfo( 454 mAppEntry.info.packageName, 455 PackageManager.MATCH_DISABLED_COMPONENTS 456 | PackageManager.MATCH_ANY_USER); 457 if (!mShowUninstalled) { 458 // If we did not start out with the app uninstalled, then 459 // it transitioning to the uninstalled state for the current 460 // user means we should go away as well. 461 return (ainfo.flags & ApplicationInfo.FLAG_INSTALLED) != 0; 462 } 463 } catch (NameNotFoundException e) { 464 return false; 465 } 466 } 467 468 return true; 469 } 470 471 @VisibleForTesting 472 AlertDialog createDialog(int id, int errorCode) { 473 switch (id) { 474 case DLG_DISABLE: 475 return new AlertDialog.Builder(getActivity()) 476 .setMessage(getActivity().getText(R.string.app_disable_dlg_text)) 477 .setPositiveButton(R.string.app_disable_dlg_positive, 478 new DialogInterface.OnClickListener() { 479 public void onClick(DialogInterface dialog, int which) { 480 // Disable the app 481 mMetricsFeatureProvider.action(getContext(), 482 MetricsEvent.ACTION_SETTINGS_DISABLE_APP); 483 new DisableChanger(AppInfoDashboardFragment.this, 484 mAppEntry.info, 485 PackageManager 486 .COMPONENT_ENABLED_STATE_DISABLED_USER) 487 .execute((Object) null); 488 } 489 }) 490 .setNegativeButton(R.string.dlg_cancel, null) 491 .create(); 492 case DLG_SPECIAL_DISABLE: 493 return new AlertDialog.Builder(getActivity()) 494 .setMessage(getActivity().getText(R.string.app_disable_dlg_text)) 495 .setPositiveButton(R.string.app_disable_dlg_positive, 496 new DialogInterface.OnClickListener() { 497 public void onClick(DialogInterface dialog, int which) { 498 // Disable the app and ask for uninstall 499 mMetricsFeatureProvider.action(getContext(), 500 MetricsEvent.ACTION_SETTINGS_DISABLE_APP); 501 uninstallPkg(mAppEntry.info.packageName, 502 false, true); 503 } 504 }) 505 .setNegativeButton(R.string.dlg_cancel, null) 506 .create(); 507 case DLG_FORCE_STOP: 508 return new AlertDialog.Builder(getActivity()) 509 .setTitle(getActivity().getText(R.string.force_stop_dlg_title)) 510 .setMessage(getActivity().getText(R.string.force_stop_dlg_text)) 511 .setPositiveButton(R.string.dlg_ok, new DialogInterface.OnClickListener() { 512 public void onClick(DialogInterface dialog, int which) { 513 // Force stop 514 forceStopPackage(mAppEntry.info.packageName); 515 } 516 }) 517 .setNegativeButton(R.string.dlg_cancel, null) 518 .create(); 519 } 520 return mInstantAppButtonPreferenceController.createDialog(id); 521 } 522 523 private void uninstallPkg(String packageName, boolean allUsers, boolean andDisable) { 524 stopListeningToPackageRemove(); 525 // Create new intent to launch Uninstaller activity 526 final Uri packageURI = Uri.parse("package:" + packageName); 527 final Intent uninstallIntent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageURI); 528 uninstallIntent.putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, allUsers); 529 mMetricsFeatureProvider.action( 530 getContext(), MetricsEvent.ACTION_SETTINGS_UNINSTALL_APP); 531 startActivityForResult(uninstallIntent, REQUEST_UNINSTALL); 532 mDisableAfterUninstall = andDisable; 533 } 534 535 private void forceStopPackage(String pkgName) { 536 mMetricsFeatureProvider.action(getContext(), MetricsEvent.ACTION_APP_FORCE_STOP, pkgName); 537 final ActivityManager am = (ActivityManager) getActivity().getSystemService( 538 Context.ACTIVITY_SERVICE); 539 Log.d(TAG, "Stopping package " + pkgName); 540 am.forceStopPackage(pkgName); 541 final int userId = UserHandle.getUserId(mAppEntry.info.uid); 542 mState.invalidatePackage(pkgName, userId); 543 final AppEntry newEnt = mState.getEntry(pkgName, userId); 544 if (newEnt != null) { 545 mAppEntry = newEnt; 546 } 547 mAppActionButtonPreferenceController.checkForceStop(mAppEntry, mPackageInfo); 548 } 549 550 public static void startAppInfoFragment(Class<?> fragment, int title, Bundle args, 551 SettingsPreferenceFragment caller, AppEntry appEntry) { 552 // start new fragment to display extended information 553 if (args == null) { 554 args = new Bundle(); 555 } 556 args.putString(ARG_PACKAGE_NAME, appEntry.info.packageName); 557 args.putInt(ARG_PACKAGE_UID, appEntry.info.uid); 558 new SubSettingLauncher(caller.getContext()) 559 .setDestination(fragment.getName()) 560 .setArguments(args) 561 .setTitle(title) 562 .setResultListener(caller, SUB_INFO_FRAGMENT) 563 .setSourceMetricsCategory(caller.getMetricsCategory()) 564 .launch(); 565 } 566 567 void handleUninstallButtonClick() { 568 if (mAppEntry == null) { 569 setIntentAndFinish(true, true); 570 return; 571 } 572 final String packageName = mAppEntry.info.packageName; 573 if (mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) { 574 stopListeningToPackageRemove(); 575 final Activity activity = getActivity(); 576 final Intent uninstallDAIntent = new Intent(activity, DeviceAdminAdd.class); 577 uninstallDAIntent.putExtra(DeviceAdminAdd.EXTRA_DEVICE_ADMIN_PACKAGE_NAME, 578 mPackageName); 579 mMetricsFeatureProvider.action( 580 activity, MetricsEvent.ACTION_SETTINGS_UNINSTALL_DEVICE_ADMIN); 581 activity.startActivityForResult(uninstallDAIntent, REQUEST_REMOVE_DEVICE_ADMIN); 582 return; 583 } 584 final EnforcedAdmin admin = RestrictedLockUtils.checkIfUninstallBlocked(getActivity(), 585 packageName, mUserId); 586 final boolean uninstallBlockedBySystem = mAppsControlDisallowedBySystem || 587 RestrictedLockUtils.hasBaseUserRestriction(getActivity(), packageName, mUserId); 588 if (admin != null && !uninstallBlockedBySystem) { 589 RestrictedLockUtils.sendShowAdminSupportDetailsIntent(getActivity(), admin); 590 } else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { 591 if (mAppEntry.info.enabled && !isDisabledUntilUsed()) { 592 // If the system app has an update and this is the only user on the device, 593 // then offer to downgrade the app, otherwise only offer to disable the 594 // app for this user. 595 if (mUpdatedSysApp && isSingleUser()) { 596 showDialogInner(DLG_SPECIAL_DISABLE, 0); 597 } else { 598 showDialogInner(DLG_DISABLE, 0); 599 } 600 } else { 601 mMetricsFeatureProvider.action( 602 getActivity(), 603 MetricsEvent.ACTION_SETTINGS_ENABLE_APP); 604 new DisableChanger(this, mAppEntry.info, 605 PackageManager.COMPONENT_ENABLED_STATE_ENABLED) 606 .execute((Object) null); 607 } 608 } else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_INSTALLED) == 0) { 609 uninstallPkg(packageName, true, false); 610 } else { 611 uninstallPkg(packageName, false, false); 612 } 613 } 614 615 void handleForceStopButtonClick() { 616 if (mAppEntry == null) { 617 setIntentAndFinish(true, true); 618 return; 619 } 620 if (mAppsControlDisallowedAdmin != null && !mAppsControlDisallowedBySystem) { 621 RestrictedLockUtils.sendShowAdminSupportDetailsIntent( 622 getActivity(), mAppsControlDisallowedAdmin); 623 } else { 624 showDialogInner(DLG_FORCE_STOP, 0); 625 //forceStopPackage(mAppInfo.packageName); 626 } 627 } 628 629 /** Returns whether there is only one user on this device, not including the system-only user */ 630 private boolean isSingleUser() { 631 final int userCount = mUserManager.getUserCount(); 632 return userCount == 1 || (mUserManager.isSplitSystemUser() && userCount == 2); 633 } 634 635 private void onPackageRemoved() { 636 getActivity().finishActivity(SUB_INFO_FRAGMENT); 637 getActivity().finishAndRemoveTask(); 638 } 639 640 @VisibleForTesting 641 int getNumberOfUserWithPackageInstalled(String packageName) { 642 final List<UserInfo> userInfos = mUserManager.getUsers(true); 643 int count = 0; 644 645 for (final UserInfo userInfo : userInfos) { 646 try { 647 // Use this API to check whether user has this package 648 final ApplicationInfo info = mPm.getApplicationInfoAsUser( 649 packageName, PackageManager.GET_META_DATA, userInfo.id); 650 if ((info.flags & ApplicationInfo.FLAG_INSTALLED) != 0) { 651 count++; 652 } 653 } catch (NameNotFoundException e) { 654 Log.e(TAG, "Package: " + packageName + " not found for user: " + userInfo.id); 655 } 656 } 657 658 return count; 659 } 660 661 private static class DisableChanger extends AsyncTask<Object, Object, Object> { 662 final PackageManager mPm; 663 final WeakReference<AppInfoDashboardFragment> mActivity; 664 final ApplicationInfo mInfo; 665 final int mState; 666 667 DisableChanger(AppInfoDashboardFragment activity, ApplicationInfo info, int state) { 668 mPm = activity.mPm; 669 mActivity = new WeakReference<AppInfoDashboardFragment>(activity); 670 mInfo = info; 671 mState = state; 672 } 673 674 @Override 675 protected Object doInBackground(Object... params) { 676 mPm.setApplicationEnabledSetting(mInfo.packageName, mState, 0); 677 return null; 678 } 679 } 680 681 private String getPackageName() { 682 if (mPackageName != null) { 683 return mPackageName; 684 } 685 final Bundle args = getArguments(); 686 mPackageName = (args != null) ? args.getString(ARG_PACKAGE_NAME) : null; 687 if (mPackageName == null) { 688 final Intent intent = (args == null) ? 689 getActivity().getIntent() : (Intent) args.getParcelable("intent"); 690 if (intent != null) { 691 mPackageName = intent.getData().getSchemeSpecificPart(); 692 } 693 } 694 return mPackageName; 695 } 696 697 @VisibleForTesting 698 void retrieveAppEntry() { 699 final Activity activity = getActivity(); 700 if (activity == null) { 701 return; 702 } 703 if (mState == null) { 704 mState = ApplicationsState.getInstance(activity.getApplication()); 705 mSession = mState.newSession(this, getLifecycle()); 706 } 707 mUserId = UserHandle.myUserId(); 708 mAppEntry = mState.getEntry(getPackageName(), UserHandle.myUserId()); 709 if (mAppEntry != null) { 710 // Get application info again to refresh changed properties of application 711 try { 712 mPackageInfo = activity.getPackageManager().getPackageInfo( 713 mAppEntry.info.packageName, 714 PackageManager.MATCH_DISABLED_COMPONENTS | 715 PackageManager.MATCH_ANY_USER | 716 PackageManager.GET_SIGNATURES | 717 PackageManager.GET_PERMISSIONS); 718 } catch (NameNotFoundException e) { 719 Log.e(TAG, "Exception when retrieving package:" + mAppEntry.info.packageName, e); 720 } 721 } else { 722 Log.w(TAG, "Missing AppEntry; maybe reinstalling?"); 723 mPackageInfo = null; 724 } 725 } 726 727 private void setIntentAndFinish(boolean finish, boolean appChanged) { 728 if (localLOGV) Log.i(TAG, "appChanged=" + appChanged); 729 final Intent intent = new Intent(); 730 intent.putExtra(ManageApplications.APP_CHG, appChanged); 731 final SettingsActivity sa = (SettingsActivity) getActivity(); 732 sa.finishPreferencePanel(Activity.RESULT_OK, intent); 733 mFinishing = true; 734 } 735 736 void showDialogInner(int id, int moveErrorCode) { 737 final DialogFragment newFragment = MyAlertDialogFragment.newInstance(id, moveErrorCode); 738 newFragment.setTargetFragment(this, 0); 739 newFragment.show(getFragmentManager(), "dialog " + id); 740 } 741 742 @Override 743 public void onRunningStateChanged(boolean running) { 744 // No op. 745 } 746 747 @Override 748 public void onRebuildComplete(ArrayList<AppEntry> apps) { 749 // No op. 750 } 751 752 @Override 753 public void onPackageIconChanged() { 754 // No op. 755 } 756 757 @Override 758 public void onAllSizesComputed() { 759 // No op. 760 } 761 762 @Override 763 public void onLauncherInfoChanged() { 764 // No op. 765 } 766 767 @Override 768 public void onLoadEntriesCompleted() { 769 // No op. 770 } 771 772 @Override 773 public void onPackageListChanged() { 774 if (!refreshUi()) { 775 setIntentAndFinish(true, true); 776 } 777 } 778 779 public static class MyAlertDialogFragment extends InstrumentedDialogFragment { 780 781 private static final String ARG_ID = "id"; 782 783 @Override 784 public int getMetricsCategory() { 785 return MetricsEvent.DIALOG_APP_INFO_ACTION; 786 } 787 788 @Override 789 public Dialog onCreateDialog(Bundle savedInstanceState) { 790 final int id = getArguments().getInt(ARG_ID); 791 final int errorCode = getArguments().getInt("moveError"); 792 final Dialog dialog = 793 ((AppInfoDashboardFragment) getTargetFragment()).createDialog(id, errorCode); 794 if (dialog == null) { 795 throw new IllegalArgumentException("unknown id " + id); 796 } 797 return dialog; 798 } 799 800 public static MyAlertDialogFragment newInstance(int id, int errorCode) { 801 final MyAlertDialogFragment dialogFragment = new MyAlertDialogFragment(); 802 final Bundle args = new Bundle(); 803 args.putInt(ARG_ID, id); 804 args.putInt("moveError", errorCode); 805 dialogFragment.setArguments(args); 806 return dialogFragment; 807 } 808 } 809 810 @VisibleForTesting 811 void startListeningToPackageRemove() { 812 if (mListeningToPackageRemove) { 813 return; 814 } 815 mListeningToPackageRemove = true; 816 final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_REMOVED); 817 filter.addDataScheme("package"); 818 getContext().registerReceiver(mPackageRemovedReceiver, filter); 819 } 820 821 private void stopListeningToPackageRemove() { 822 if (!mListeningToPackageRemove) { 823 return; 824 } 825 mListeningToPackageRemove = false; 826 getContext().unregisterReceiver(mPackageRemovedReceiver); 827 } 828 829 @VisibleForTesting 830 final BroadcastReceiver mPackageRemovedReceiver = new BroadcastReceiver() { 831 @Override 832 public void onReceive(Context context, Intent intent) { 833 final String packageName = intent.getData().getSchemeSpecificPart(); 834 if (!mFinishing && (mAppEntry == null || mAppEntry.info == null 835 || TextUtils.equals(mAppEntry.info.packageName, packageName))) { 836 onPackageRemoved(); 837 } 838 } 839 }; 840 841} 842