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