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