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