AppButtonsPreferenceController.java revision 0b83954f1c86a5269b4b863e6ad0958dba03fed9
1/* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy 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, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.settings.fuelgauge; 18 19import android.app.Activity; 20import android.app.ActivityManager; 21import android.app.Fragment; 22import android.content.BroadcastReceiver; 23import android.content.ComponentName; 24import android.content.Context; 25import android.content.Intent; 26import android.content.IntentFilter; 27import android.content.pm.ApplicationInfo; 28import android.content.pm.PackageInfo; 29import android.content.pm.PackageManager; 30import android.content.pm.ResolveInfo; 31import android.content.pm.UserInfo; 32import android.content.res.Resources; 33import android.net.Uri; 34import android.os.AsyncTask; 35import android.os.Bundle; 36import android.os.RemoteException; 37import android.os.ServiceManager; 38import android.os.UserHandle; 39import android.os.UserManager; 40import android.support.v7.preference.PreferenceScreen; 41import android.util.Log; 42import android.view.View; 43import android.webkit.IWebViewUpdateService; 44import android.widget.Button; 45 46import com.android.internal.annotations.VisibleForTesting; 47import com.android.internal.logging.nano.MetricsProto; 48import com.android.settings.DeviceAdminAdd; 49import com.android.settings.R; 50import com.android.settings.SettingsActivity; 51import com.android.settings.Utils; 52import com.android.settings.applications.LayoutPreference; 53import com.android.settings.core.PreferenceController; 54import com.android.settings.core.instrumentation.MetricsFeatureProvider; 55import com.android.settings.core.lifecycle.Lifecycle; 56import com.android.settings.core.lifecycle.LifecycleObserver; 57import com.android.settings.core.lifecycle.events.OnDestroy; 58import com.android.settings.core.lifecycle.events.OnPause; 59import com.android.settings.core.lifecycle.events.OnResume; 60import com.android.settings.enterprise.DevicePolicyManagerWrapper; 61import com.android.settings.overlay.FeatureFactory; 62import com.android.settingslib.RestrictedLockUtils; 63import com.android.settingslib.applications.AppUtils; 64import com.android.settingslib.applications.ApplicationsState; 65 66import java.util.ArrayList; 67import java.util.HashSet; 68import java.util.List; 69 70/** 71 * Controller to control the uninstall button and forcestop button. All fragments that use 72 * this controller should implement {@link ButtonActionDialogFragment.AppButtonsDialogListener} and 73 * handle {@link Fragment#onActivityResult(int, int, Intent)} 74 * 75 * An easy way to handle them is to delegate them to {@link #handleDialogClick(int)} and 76 * {@link #handleActivityResult(int, int, Intent)} in this controller. 77 */ 78//TODO(b/35810915): Make InstalledAppDetails use this controller 79public class AppButtonsPreferenceController extends PreferenceController implements 80 LifecycleObserver, OnResume, OnPause, OnDestroy, View.OnClickListener, 81 ApplicationsState.Callbacks { 82 public static final String APP_CHG = "chg"; 83 84 private static final String TAG = "AppButtonsPrefCtl"; 85 private static final String KEY_ACTION_BUTTONS = "action_buttons"; 86 private static final boolean LOCAL_LOGV = false; 87 88 @VisibleForTesting 89 final HashSet<String> mHomePackages = new HashSet<>(); 90 @VisibleForTesting 91 ApplicationsState mState; 92 @VisibleForTesting 93 ApplicationsState.AppEntry mAppEntry; 94 @VisibleForTesting 95 PackageInfo mPackageInfo; 96 @VisibleForTesting 97 Button mForceStopButton; 98 @VisibleForTesting 99 Button mUninstallButton; 100 @VisibleForTesting 101 String mPackageName; 102 @VisibleForTesting 103 boolean mDisableAfterUninstall = false; 104 105 private final int mRequestUninstall; 106 private final int mRequestRemoveDeviceAdmin; 107 108 private ApplicationsState.Session mSession; 109 private DevicePolicyManagerWrapper mDpm; 110 private UserManager mUserManager; 111 private PackageManager mPm; 112 private SettingsActivity mActivity; 113 private Fragment mFragment; 114 private RestrictedLockUtils.EnforcedAdmin mAppsControlDisallowedAdmin; 115 private MetricsFeatureProvider mMetricsFeatureProvider; 116 117 private LayoutPreference mButtonsPref; 118 private int mUserId; 119 private boolean mUpdatedSysApp = false; 120 private boolean mListeningToPackageRemove = false; 121 private boolean mFinishing = false; 122 private boolean mAppsControlDisallowedBySystem; 123 124 public AppButtonsPreferenceController(SettingsActivity activity, Fragment fragment, 125 Lifecycle lifecycle, String packageName, ApplicationsState state, 126 DevicePolicyManagerWrapper dpm, UserManager userManager, 127 PackageManager packageManager, int requestUninstall, int requestRemoveDeviceAdmin) { 128 super(activity); 129 130 if (!(fragment instanceof ButtonActionDialogFragment.AppButtonsDialogListener)) { 131 throw new IllegalArgumentException( 132 "Fragment should implement AppButtonsDialogListener"); 133 } 134 135 mMetricsFeatureProvider = FeatureFactory.getFactory(activity).getMetricsFeatureProvider(); 136 137 mState = state; 138 mDpm = dpm; 139 mUserManager = userManager; 140 mPm = packageManager; 141 mPackageName = packageName; 142 mActivity = activity; 143 mFragment = fragment; 144 mUserId = UserHandle.myUserId(); 145 mRequestUninstall = requestUninstall; 146 mRequestRemoveDeviceAdmin = requestRemoveDeviceAdmin; 147 148 if (packageName != null) { 149 mAppEntry = mState.getEntry(packageName, mUserId); 150 mSession = mState.newSession(this); 151 lifecycle.addObserver(this); 152 } else { 153 mFinishing = true; 154 } 155 } 156 157 @Override 158 public boolean isAvailable() { 159 // TODO(b/37313605): Re-enable once this controller supports instant apps 160 return mAppEntry != null && !AppUtils.isInstant(mAppEntry.info); 161 } 162 163 @Override 164 public void displayPreference(PreferenceScreen screen) { 165 super.displayPreference(screen); 166 if (isAvailable()) { 167 mButtonsPref = (LayoutPreference) screen.findPreference(KEY_ACTION_BUTTONS); 168 169 mUninstallButton = (Button) mButtonsPref.findViewById(R.id.left_button); 170 mUninstallButton.setText(R.string.uninstall_text); 171 172 mForceStopButton = (Button) mButtonsPref.findViewById(R.id.right_button); 173 mForceStopButton.setText(R.string.force_stop); 174 mForceStopButton.setEnabled(false); 175 } 176 } 177 178 @Override 179 public String getPreferenceKey() { 180 return KEY_ACTION_BUTTONS; 181 } 182 183 @Override 184 public void onResume() { 185 mSession.resume(); 186 if (isAvailable() && !mFinishing) { 187 mAppsControlDisallowedBySystem = RestrictedLockUtils.hasBaseUserRestriction(mActivity, 188 UserManager.DISALLOW_APPS_CONTROL, mUserId); 189 mAppsControlDisallowedAdmin = RestrictedLockUtils.checkIfRestrictionEnforced(mActivity, 190 UserManager.DISALLOW_APPS_CONTROL, mUserId); 191 192 if (!refreshUi()) { 193 setIntentAndFinish(true); 194 } 195 } 196 } 197 198 @Override 199 public void onPause() { 200 mSession.pause(); 201 } 202 203 @Override 204 public void onDestroy() { 205 stopListeningToPackageRemove(); 206 mSession.release(); 207 } 208 209 @Override 210 public void onClick(View v) { 211 final String packageName = mAppEntry.info.packageName; 212 final int id = v.getId(); 213 if (id == R.id.left_button) { 214 // Uninstall 215 if (mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) { 216 stopListeningToPackageRemove(); 217 Intent uninstallDaIntent = new Intent(mActivity, DeviceAdminAdd.class); 218 uninstallDaIntent.putExtra(DeviceAdminAdd.EXTRA_DEVICE_ADMIN_PACKAGE_NAME, 219 packageName); 220 mMetricsFeatureProvider.action(mActivity, 221 MetricsProto.MetricsEvent.ACTION_SETTINGS_UNINSTALL_DEVICE_ADMIN); 222 mFragment.startActivityForResult(uninstallDaIntent, mRequestRemoveDeviceAdmin); 223 return; 224 } 225 RestrictedLockUtils.EnforcedAdmin admin = 226 RestrictedLockUtils.checkIfUninstallBlocked(mActivity, 227 packageName, mUserId); 228 boolean uninstallBlockedBySystem = mAppsControlDisallowedBySystem || 229 RestrictedLockUtils.hasBaseUserRestriction(mActivity, packageName, mUserId); 230 if (admin != null && !uninstallBlockedBySystem) { 231 RestrictedLockUtils.sendShowAdminSupportDetailsIntent(mActivity, admin); 232 } else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { 233 if (mAppEntry.info.enabled && !isDisabledUntilUsed()) { 234 // If the system app has an update and this is the only user on the device, 235 // then offer to downgrade the app, otherwise only offer to disable the 236 // app for this user. 237 if (mUpdatedSysApp && isSingleUser()) { 238 showDialogInner(ButtonActionDialogFragment.DialogType.SPECIAL_DISABLE); 239 } else { 240 showDialogInner(ButtonActionDialogFragment.DialogType.DISABLE); 241 } 242 } else { 243 mMetricsFeatureProvider.action( 244 mActivity, 245 mAppEntry.info.enabled 246 ? MetricsProto.MetricsEvent.ACTION_SETTINGS_DISABLE_APP 247 : MetricsProto.MetricsEvent.ACTION_SETTINGS_ENABLE_APP); 248 AsyncTask.execute(new DisableChangerRunnable(mPm, mAppEntry.info.packageName, 249 PackageManager.COMPONENT_ENABLED_STATE_DEFAULT)); 250 } 251 } else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_INSTALLED) == 0) { 252 uninstallPkg(packageName, true, false); 253 } else { 254 uninstallPkg(packageName, false, false); 255 } 256 } else if (id == R.id.right_button) { 257 // force stop 258 if (mAppsControlDisallowedAdmin != null && !mAppsControlDisallowedBySystem) { 259 RestrictedLockUtils.sendShowAdminSupportDetailsIntent( 260 mActivity, mAppsControlDisallowedAdmin); 261 } else { 262 showDialogInner(ButtonActionDialogFragment.DialogType.FORCE_STOP); 263 } 264 } 265 } 266 267 public void handleActivityResult(int requestCode, int resultCode, Intent data) { 268 if (requestCode == mRequestUninstall) { 269 if (mDisableAfterUninstall) { 270 mDisableAfterUninstall = false; 271 AsyncTask.execute(new DisableChangerRunnable(mPm, mAppEntry.info.packageName, 272 PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER)); 273 } 274 refreshAndFinishIfPossible(); 275 } else if (requestCode == mRequestRemoveDeviceAdmin) { 276 refreshAndFinishIfPossible(); 277 } 278 } 279 280 public void handleDialogClick(int id) { 281 switch (id) { 282 case ButtonActionDialogFragment.DialogType.DISABLE: 283 mMetricsFeatureProvider.action(mActivity, 284 MetricsProto.MetricsEvent.ACTION_SETTINGS_DISABLE_APP); 285 AsyncTask.execute(new DisableChangerRunnable(mPm, mAppEntry.info.packageName, 286 PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER)); 287 break; 288 case ButtonActionDialogFragment.DialogType.SPECIAL_DISABLE: 289 mMetricsFeatureProvider.action(mActivity, 290 MetricsProto.MetricsEvent.ACTION_SETTINGS_DISABLE_APP); 291 uninstallPkg(mAppEntry.info.packageName, false, true); 292 break; 293 case ButtonActionDialogFragment.DialogType.FORCE_STOP: 294 forceStopPackage(mAppEntry.info.packageName); 295 break; 296 } 297 } 298 299 @Override 300 public void onRunningStateChanged(boolean running) { 301 302 } 303 304 @Override 305 public void onPackageListChanged() { 306 refreshUi(); 307 } 308 309 @Override 310 public void onRebuildComplete(ArrayList<ApplicationsState.AppEntry> apps) { 311 312 } 313 314 @Override 315 public void onPackageIconChanged() { 316 317 } 318 319 @Override 320 public void onPackageSizeChanged(String packageName) { 321 322 } 323 324 @Override 325 public void onAllSizesComputed() { 326 327 } 328 329 @Override 330 public void onLauncherInfoChanged() { 331 332 } 333 334 @Override 335 public void onLoadEntriesCompleted() { 336 337 } 338 339 @VisibleForTesting 340 void retrieveAppEntry() { 341 mAppEntry = mState.getEntry(mPackageName, mUserId); 342 if (mAppEntry != null) { 343 try { 344 mPackageInfo = mPm.getPackageInfo(mAppEntry.info.packageName, 345 PackageManager.MATCH_DISABLED_COMPONENTS | 346 PackageManager.MATCH_ANY_USER | 347 PackageManager.GET_SIGNATURES | 348 PackageManager.GET_PERMISSIONS); 349 350 mPackageName = mAppEntry.info.packageName; 351 } catch (PackageManager.NameNotFoundException e) { 352 Log.e(TAG, "Exception when retrieving package:" + mAppEntry.info.packageName, e); 353 mPackageInfo = null; 354 } 355 } else { 356 mPackageInfo = null; 357 } 358 } 359 360 @VisibleForTesting 361 void updateUninstallButton() { 362 final boolean isBundled = (mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0; 363 boolean enabled = true; 364 if (isBundled) { 365 enabled = handleDisableable(mUninstallButton); 366 } else { 367 if ((mPackageInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) == 0 368 && mUserManager.getUsers().size() >= 2) { 369 // When we have multiple users, there is a separate menu 370 // to uninstall for all users. 371 enabled = false; 372 } 373 } 374 // If this is a device admin, it can't be uninstalled or disabled. 375 // We do this here so the text of the button is still set correctly. 376 if (isBundled && mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) { 377 enabled = false; 378 } 379 380 // We don't allow uninstalling DO/PO on *any* users, because if it's a system app, 381 // "uninstall" is actually "downgrade to the system version + disable", and "downgrade" 382 // will clear data on all users. 383 if (isProfileOrDeviceOwner(mPackageInfo.packageName)) { 384 enabled = false; 385 } 386 387 // Don't allow uninstalling the device provisioning package. 388 if (Utils.isDeviceProvisioningPackage(mContext.getResources(), 389 mAppEntry.info.packageName)) { 390 enabled = false; 391 } 392 393 // If the uninstall intent is already queued, disable the uninstall button 394 if (mDpm.isUninstallInQueue(mPackageName)) { 395 enabled = false; 396 } 397 398 // Home apps need special handling. Bundled ones we don't risk downgrading 399 // because that can interfere with home-key resolution. Furthermore, we 400 // can't allow uninstallation of the only home app, and we don't want to 401 // allow uninstallation of an explicitly preferred one -- the user can go 402 // to Home settings and pick a different one, after which we'll permit 403 // uninstallation of the now-not-default one. 404 if (enabled && mHomePackages.contains(mPackageInfo.packageName)) { 405 if (isBundled) { 406 enabled = false; 407 } else { 408 ArrayList<ResolveInfo> homeActivities = new ArrayList<ResolveInfo>(); 409 ComponentName currentDefaultHome = mPm.getHomeActivities(homeActivities); 410 if (currentDefaultHome == null) { 411 // No preferred default, so permit uninstall only when 412 // there is more than one candidate 413 enabled = (mHomePackages.size() > 1); 414 } else { 415 // There is an explicit default home app -- forbid uninstall of 416 // that one, but permit it for installed-but-inactive ones. 417 enabled = !mPackageInfo.packageName.equals(currentDefaultHome.getPackageName()); 418 } 419 } 420 } 421 422 if (mAppsControlDisallowedBySystem) { 423 enabled = false; 424 } 425 426 if (isFallbackPackage(mAppEntry.info.packageName)) { 427 enabled = false; 428 } 429 430 mUninstallButton.setEnabled(enabled); 431 if (enabled) { 432 // Register listener 433 mUninstallButton.setOnClickListener(this); 434 } 435 } 436 437 /** 438 * Finish this fragment and return data if possible 439 */ 440 private void setIntentAndFinish(boolean appChanged) { 441 if (LOCAL_LOGV) { 442 Log.i(TAG, "appChanged=" + appChanged); 443 } 444 Intent intent = new Intent(); 445 intent.putExtra(APP_CHG, appChanged); 446 mActivity.finishPreferencePanel(mFragment, Activity.RESULT_OK, intent); 447 mFinishing = true; 448 } 449 450 private void refreshAndFinishIfPossible() { 451 if (!refreshUi()) { 452 setIntentAndFinish(true); 453 } else { 454 startListeningToPackageRemove(); 455 } 456 } 457 458 @VisibleForTesting 459 boolean isFallbackPackage(String packageName) { 460 try { 461 IWebViewUpdateService webviewUpdateService = 462 IWebViewUpdateService.Stub.asInterface( 463 ServiceManager.getService("webviewupdate")); 464 if (webviewUpdateService.isFallbackPackage(packageName)) { 465 return true; 466 } 467 } catch (RemoteException e) { 468 throw new RuntimeException(e); 469 } 470 471 return false; 472 } 473 474 @VisibleForTesting 475 void updateForceStopButton() { 476 if (mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) { 477 // User can't force stop device admin. 478 Log.w(TAG, "User can't force stop device admin"); 479 updateForceStopButtonInner(false); 480 } else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_STOPPED) == 0) { 481 // If the app isn't explicitly stopped, then always show the 482 // force stop button. 483 Log.w(TAG, "App is not explicitly stopped"); 484 updateForceStopButtonInner(true); 485 } else { 486 Intent intent = new Intent(Intent.ACTION_QUERY_PACKAGE_RESTART, 487 Uri.fromParts("package", mAppEntry.info.packageName, null)); 488 intent.putExtra(Intent.EXTRA_PACKAGES, new String[]{mAppEntry.info.packageName}); 489 intent.putExtra(Intent.EXTRA_UID, mAppEntry.info.uid); 490 intent.putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(mAppEntry.info.uid)); 491 Log.d(TAG, "Sending broadcast to query restart status for " 492 + mAppEntry.info.packageName); 493 mActivity.sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT, null, 494 mCheckKillProcessesReceiver, null, Activity.RESULT_CANCELED, null, null); 495 } 496 } 497 498 @VisibleForTesting 499 void updateForceStopButtonInner(boolean enabled) { 500 if (mAppsControlDisallowedBySystem) { 501 mForceStopButton.setEnabled(false); 502 } else { 503 mForceStopButton.setEnabled(enabled); 504 mForceStopButton.setOnClickListener(this); 505 } 506 } 507 508 @VisibleForTesting 509 void uninstallPkg(String packageName, boolean allUsers, boolean andDisable) { 510 stopListeningToPackageRemove(); 511 // Create new intent to launch Uninstaller activity 512 Uri packageUri = Uri.parse("package:" + packageName); 513 Intent uninstallIntent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageUri); 514 uninstallIntent.putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, allUsers); 515 516 mMetricsFeatureProvider.action( 517 mActivity, MetricsProto.MetricsEvent.ACTION_SETTINGS_UNINSTALL_APP); 518 mFragment.startActivityForResult(uninstallIntent, mRequestUninstall); 519 mDisableAfterUninstall = andDisable; 520 } 521 522 @VisibleForTesting 523 void forceStopPackage(String pkgName) { 524 FeatureFactory.getFactory(mContext).getMetricsFeatureProvider().action(mContext, 525 MetricsProto.MetricsEvent.ACTION_APP_FORCE_STOP, pkgName); 526 ActivityManager am = (ActivityManager) mActivity.getSystemService( 527 Context.ACTIVITY_SERVICE); 528 Log.d(TAG, "Stopping package " + pkgName); 529 am.forceStopPackage(pkgName); 530 int userId = UserHandle.getUserId(mAppEntry.info.uid); 531 mState.invalidatePackage(pkgName, userId); 532 ApplicationsState.AppEntry newEnt = mState.getEntry(pkgName, userId); 533 if (newEnt != null) { 534 mAppEntry = newEnt; 535 } 536 updateForceStopButton(); 537 } 538 539 @VisibleForTesting 540 boolean handleDisableable(Button button) { 541 boolean disableable = false; 542 // Try to prevent the user from bricking their phone 543 // by not allowing disabling of apps signed with the 544 // system cert and any launcher app in the system. 545 if (mHomePackages.contains(mAppEntry.info.packageName) 546 || isSystemPackage(mActivity.getResources(), mPm, mPackageInfo)) { 547 // Disable button for core system applications. 548 button.setText(R.string.disable_text); 549 } else if (mAppEntry.info.enabled && !isDisabledUntilUsed()) { 550 button.setText(R.string.disable_text); 551 disableable = true; 552 } else { 553 button.setText(R.string.enable_text); 554 disableable = true; 555 } 556 557 return disableable; 558 } 559 560 @VisibleForTesting 561 boolean isSystemPackage(Resources resources, PackageManager pm, PackageInfo packageInfo) { 562 return Utils.isSystemPackage(resources, pm, packageInfo); 563 } 564 565 private boolean isDisabledUntilUsed() { 566 return mAppEntry.info.enabledSetting 567 == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED; 568 } 569 570 private void showDialogInner(@ButtonActionDialogFragment.DialogType int id) { 571 ButtonActionDialogFragment newFragment = ButtonActionDialogFragment.newInstance(id); 572 newFragment.setTargetFragment(mFragment, 0); 573 newFragment.show(mActivity.getFragmentManager(), "dialog " + id); 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 580 || (mUserManager.isSplitSystemUser() && userCount == 2); 581 } 582 583 /** Returns if the supplied package is device owner or profile owner of at least one user */ 584 private boolean isProfileOrDeviceOwner(String packageName) { 585 List<UserInfo> userInfos = mUserManager.getUsers(); 586 if (mDpm.isDeviceOwnerAppOnAnyUser(packageName)) { 587 return true; 588 } 589 for (int i = 0, size = userInfos.size(); i < size; i++) { 590 ComponentName cn = mDpm.getProfileOwnerAsUser(userInfos.get(i).id); 591 if (cn != null && cn.getPackageName().equals(packageName)) { 592 return true; 593 } 594 } 595 return false; 596 } 597 598 private final BroadcastReceiver mCheckKillProcessesReceiver = new BroadcastReceiver() { 599 @Override 600 public void onReceive(Context context, Intent intent) { 601 final boolean enabled = getResultCode() != Activity.RESULT_CANCELED; 602 Log.d(TAG, "Got broadcast response: Restart status for " 603 + mAppEntry.info.packageName + " " + enabled); 604 updateForceStopButtonInner(enabled); 605 } 606 }; 607 608 private boolean signaturesMatch(String pkg1, String pkg2) { 609 if (pkg1 != null && pkg2 != null) { 610 try { 611 final int match = mPm.checkSignatures(pkg1, pkg2); 612 if (match >= PackageManager.SIGNATURE_MATCH) { 613 return true; 614 } 615 } catch (Exception e) { 616 // e.g. named alternate package not found during lookup; 617 // this is an expected case sometimes 618 } 619 } 620 return false; 621 } 622 623 @VisibleForTesting 624 boolean refreshUi() { 625 if (mPackageName == null) { 626 return false; 627 } 628 retrieveAppEntry(); 629 if (mAppEntry == null || mPackageInfo == null) { 630 return false; 631 } 632 // Get list of "home" apps and trace through any meta-data references 633 List<ResolveInfo> homeActivities = new ArrayList<>(); 634 mPm.getHomeActivities(homeActivities); 635 mHomePackages.clear(); 636 for (int i = 0, size = homeActivities.size(); i < size; i++) { 637 ResolveInfo ri = homeActivities.get(i); 638 final String activityPkg = ri.activityInfo.packageName; 639 mHomePackages.add(activityPkg); 640 641 // Also make sure to include anything proxying for the home app 642 final Bundle metadata = ri.activityInfo.metaData; 643 if (metadata != null) { 644 final String metaPkg = metadata.getString(ActivityManager.META_HOME_ALTERNATE); 645 if (signaturesMatch(metaPkg, activityPkg)) { 646 mHomePackages.add(metaPkg); 647 } 648 } 649 } 650 651 updateUninstallButton(); 652 updateForceStopButton(); 653 654 return true; 655 } 656 657 private void startListeningToPackageRemove() { 658 if (mListeningToPackageRemove) { 659 return; 660 } 661 mListeningToPackageRemove = true; 662 final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_REMOVED); 663 filter.addDataScheme("package"); 664 mActivity.registerReceiver(mPackageRemovedReceiver, filter); 665 } 666 667 private void stopListeningToPackageRemove() { 668 if (!mListeningToPackageRemove) { 669 return; 670 } 671 mListeningToPackageRemove = false; 672 mActivity.unregisterReceiver(mPackageRemovedReceiver); 673 } 674 675 676 /** 677 * Changes the status of disable/enable for a package 678 */ 679 private class DisableChangerRunnable implements Runnable { 680 final PackageManager mPm; 681 final String mPackageName; 682 final int mState; 683 684 public DisableChangerRunnable(PackageManager pm, String packageName, int state) { 685 mPm = pm; 686 mPackageName = packageName; 687 mState = state; 688 } 689 690 @Override 691 public void run() { 692 mPm.setApplicationEnabledSetting(mPackageName, mState, 0); 693 } 694 } 695 696 /** 697 * Receiver to listen to the remove action for packages 698 */ 699 private final BroadcastReceiver mPackageRemovedReceiver = new BroadcastReceiver() { 700 @Override 701 public void onReceive(Context context, Intent intent) { 702 String packageName = intent.getData().getSchemeSpecificPart(); 703 if (!mFinishing && mAppEntry.info.packageName.equals(packageName)) { 704 mActivity.finishAndRemoveTask(); 705 } 706 } 707 }; 708 709} 710