1 /* 2 * Copyright (C) 2020 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 17 package com.android.car.settings.applications; 18 19 import static android.app.Activity.RESULT_FIRST_USER; 20 import static android.app.Activity.RESULT_OK; 21 22 import static com.android.car.settings.applications.ApplicationsUtils.isKeepEnabledPackage; 23 import static com.android.car.settings.applications.ApplicationsUtils.isProfileOrDeviceOwner; 24 import static com.android.car.settings.common.ActionButtonsPreference.ActionButtons; 25 import static com.android.car.settings.enterprise.ActionDisabledByAdminDialogFragment.DISABLED_BY_ADMIN_CONFIRM_DIALOG_TAG; 26 import static com.android.car.settings.enterprise.EnterpriseUtils.BLOCKED_UNINSTALL_APP; 27 28 import android.app.Activity; 29 import android.app.ActivityManager; 30 import android.app.admin.DevicePolicyManager; 31 import android.car.drivingstate.CarUxRestrictions; 32 import android.content.BroadcastReceiver; 33 import android.content.ComponentName; 34 import android.content.Context; 35 import android.content.Intent; 36 import android.content.pm.ApplicationInfo; 37 import android.content.pm.PackageInfo; 38 import android.content.pm.PackageManager; 39 import android.content.pm.ResolveInfo; 40 import android.net.Uri; 41 import android.os.Bundle; 42 import android.os.UserHandle; 43 import android.os.UserManager; 44 import android.util.ArraySet; 45 import android.view.View; 46 import android.widget.Toast; 47 48 import androidx.annotation.Nullable; 49 import androidx.annotation.VisibleForTesting; 50 51 import com.android.car.settings.R; 52 import com.android.car.settings.common.ActionButtonInfo; 53 import com.android.car.settings.common.ActionButtonsPreference; 54 import com.android.car.settings.common.ActivityResultCallback; 55 import com.android.car.settings.common.ConfirmationDialogFragment; 56 import com.android.car.settings.common.FragmentController; 57 import com.android.car.settings.common.Logger; 58 import com.android.car.settings.common.PreferenceController; 59 import com.android.car.settings.enterprise.ActionDisabledByAdminDialogFragment; 60 import com.android.car.settings.enterprise.DeviceAdminAddActivity; 61 import com.android.car.settings.enterprise.EnterpriseUtils; 62 import com.android.car.settings.profiles.ProfileHelper; 63 import com.android.settingslib.Utils; 64 import com.android.settingslib.applications.ApplicationsState; 65 66 import java.util.ArrayList; 67 import java.util.Arrays; 68 import java.util.List; 69 import java.util.Set; 70 71 /** 72 * Shows actions associated with an application, like uninstall and forceStop. 73 * 74 * <p>To uninstall an app, it must <i>not</i> be: 75 * <ul> 76 * <li>a system bundled app 77 * <li>system signed 78 * <li>managed by an active admin from a device policy 79 * <li>a device or profile owner 80 * <li>the only home app 81 * <li>the default home app 82 * <li>for a user with the {@link UserManager#DISALLOW_APPS_CONTROL} restriction 83 * <li>for a user with the {@link UserManager#DISALLOW_UNINSTALL_APPS} restriction 84 * </ul> 85 * 86 * <p>For apps that cannot be uninstalled, a disable option is shown instead (or enable if the app 87 * is already disabled). 88 */ 89 public class ApplicationActionButtonsPreferenceController extends 90 PreferenceController<ActionButtonsPreference> implements ActivityResultCallback { 91 private static final Logger LOG = new Logger( 92 ApplicationActionButtonsPreferenceController.class); 93 94 private static final List<String> FORCE_STOP_RESTRICTIONS = 95 Arrays.asList(UserManager.DISALLOW_APPS_CONTROL); 96 private static final List<String> UNINSTALL_RESTRICTIONS = 97 Arrays.asList(UserManager.DISALLOW_UNINSTALL_APPS, UserManager.DISALLOW_APPS_CONTROL); 98 private static final List<String> DISABLE_RESTRICTIONS = 99 Arrays.asList(UserManager.DISALLOW_APPS_CONTROL); 100 101 @VisibleForTesting 102 static final String DISABLE_CONFIRM_DIALOG_TAG = 103 "com.android.car.settings.applications.DisableConfirmDialog"; 104 @VisibleForTesting 105 static final String FORCE_STOP_CONFIRM_DIALOG_TAG = 106 "com.android.car.settings.applications.ForceStopConfirmDialog"; 107 108 @VisibleForTesting 109 static final int UNINSTALL_REQUEST_CODE = 10; 110 111 @VisibleForTesting 112 static final int UNINSTALL_DEVICE_ADMIN_REQUEST_CODE = 11; 113 114 private DevicePolicyManager mDpm; 115 private PackageManager mPm; 116 private UserManager mUserManager; 117 private ProfileHelper mProfileHelper; 118 private ApplicationsState.Session mSession; 119 120 private ApplicationsState.AppEntry mAppEntry; 121 private ApplicationsState mApplicationsState; 122 private String mPackageName; 123 private PackageInfo mPackageInfo; 124 125 private String mRestriction; 126 127 @VisibleForTesting 128 final ConfirmationDialogFragment.ConfirmListener mForceStopConfirmListener = 129 new ConfirmationDialogFragment.ConfirmListener() { 130 @Override 131 public void onConfirm(@Nullable Bundle arguments) { 132 LOG.d("Stopping package " + mPackageName); 133 getContext().getSystemService(ActivityManager.class) 134 .forceStopPackage(mPackageName); 135 int userId = UserHandle.getUserId(mAppEntry.info.uid); 136 mApplicationsState.invalidatePackage(mPackageName, userId); 137 Toast.makeText(getContext(), getContext().getResources() 138 .getString(R.string.force_stop_success_toast_text, 139 mAppEntry.info.loadLabel(mPm)), Toast.LENGTH_LONG).show(); 140 } 141 }; 142 143 private final View.OnClickListener mForceStopClickListener = i -> { 144 if (ignoreActionBecauseItsDisabledByAdmin(FORCE_STOP_RESTRICTIONS)) return; 145 ConfirmationDialogFragment dialogFragment = 146 new ConfirmationDialogFragment.Builder(getContext()) 147 .setTitle(R.string.force_stop_dialog_title) 148 .setMessage(R.string.force_stop_dialog_text) 149 .setPositiveButton(android.R.string.ok, 150 mForceStopConfirmListener) 151 .setNegativeButton(android.R.string.cancel, /* rejectListener= */ null) 152 .build(); 153 getFragmentController().showDialog(dialogFragment, FORCE_STOP_CONFIRM_DIALOG_TAG); 154 }; 155 156 @VisibleForTesting 157 final BroadcastReceiver mCheckKillProcessesReceiver = new BroadcastReceiver() { 158 @Override 159 public void onReceive(Context context, Intent intent) { 160 boolean enabled = getResultCode() != Activity.RESULT_CANCELED; 161 LOG.d("Got broadcast response: Restart status for " + mPackageName + " " + enabled); 162 updateForceStopButtonInner(enabled); 163 } 164 }; 165 166 @VisibleForTesting 167 final ConfirmationDialogFragment.ConfirmListener mDisableConfirmListener = i -> { 168 mPm.setApplicationEnabledSetting(mPackageName, 169 PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER, /* flags= */ 0); 170 updateUninstallButtonInner(false); 171 }; 172 173 private final View.OnClickListener mDisableClickListener = i -> { 174 if (ignoreActionBecauseItsDisabledByAdmin(DISABLE_RESTRICTIONS)) return; 175 ConfirmationDialogFragment dialogFragment = 176 new ConfirmationDialogFragment.Builder(getContext()) 177 .setMessage(getContext().getString(R.string.app_disable_dialog_text)) 178 .setPositiveButton(R.string.app_disable_dialog_positive, 179 mDisableConfirmListener) 180 .setNegativeButton(android.R.string.cancel, /* rejectListener= */ null) 181 .build(); 182 getFragmentController().showDialog(dialogFragment, DISABLE_CONFIRM_DIALOG_TAG); 183 }; 184 185 private final View.OnClickListener mEnableClickListener = i -> { 186 if (ignoreActionBecauseItsDisabledByAdmin(DISABLE_RESTRICTIONS)) return; 187 mPm.setApplicationEnabledSetting(mPackageName, 188 PackageManager.COMPONENT_ENABLED_STATE_DEFAULT, /* flags= */ 0); 189 updateUninstallButtonInner(true); 190 }; 191 192 private final View.OnClickListener mUninstallClickListener = i -> { 193 if (ignoreActionBecauseItsDisabledByAdmin(UNINSTALL_RESTRICTIONS)) return; 194 Uri packageUri = Uri.parse("package:" + mPackageName); 195 if (mDpm.packageHasActiveAdmins(mPackageName)) { 196 // Show Device Admin app details screen to deactivate the device admin before it can 197 // be uninstalled. 198 Intent deviceAdminIntent = new Intent(getContext(), DeviceAdminAddActivity.class) 199 .putExtra(DeviceAdminAddActivity.EXTRA_DEVICE_ADMIN_PACKAGE_NAME, mPackageName); 200 getFragmentController().startActivityForResult(deviceAdminIntent, 201 UNINSTALL_DEVICE_ADMIN_REQUEST_CODE, /* callback= */ this); 202 } else { 203 Intent uninstallIntent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageUri); 204 uninstallIntent.putExtra(Intent.EXTRA_RETURN_RESULT, true); 205 getFragmentController().startActivityForResult(uninstallIntent, UNINSTALL_REQUEST_CODE, 206 /* callback= */ this); 207 } 208 }; 209 210 private final ApplicationsState.Callbacks mApplicationStateCallbacks = 211 new ApplicationsState.Callbacks() { 212 @Override 213 public void onRunningStateChanged(boolean running) { 214 } 215 216 @Override 217 public void onPackageListChanged() { 218 refreshUi(); 219 } 220 221 @Override 222 public void onRebuildComplete(ArrayList<ApplicationsState.AppEntry> apps) { 223 } 224 225 @Override 226 public void onPackageIconChanged() { 227 } 228 229 @Override 230 public void onPackageSizeChanged(String packageName) { 231 } 232 233 @Override 234 public void onAllSizesComputed() { 235 } 236 237 @Override 238 public void onLauncherInfoChanged() { 239 } 240 241 @Override 242 public void onLoadEntriesCompleted() { 243 } 244 }; 245 ApplicationActionButtonsPreferenceController(Context context, String preferenceKey, FragmentController fragmentController, CarUxRestrictions uxRestrictions)246 public ApplicationActionButtonsPreferenceController(Context context, String preferenceKey, 247 FragmentController fragmentController, CarUxRestrictions uxRestrictions) { 248 super(context, preferenceKey, fragmentController, uxRestrictions); 249 mDpm = context.getSystemService(DevicePolicyManager.class); 250 mPm = context.getPackageManager(); 251 mUserManager = UserManager.get(context); 252 mProfileHelper = ProfileHelper.getInstance(context); 253 } 254 255 @Override getPreferenceType()256 protected Class<ActionButtonsPreference> getPreferenceType() { 257 return ActionButtonsPreference.class; 258 } 259 260 /** Sets the {@link ApplicationsState.AppEntry} which is used to load the app name and icon. */ setAppEntry( ApplicationsState.AppEntry appEntry)261 public ApplicationActionButtonsPreferenceController setAppEntry( 262 ApplicationsState.AppEntry appEntry) { 263 mAppEntry = appEntry; 264 return this; 265 } 266 267 /** Sets the {@link ApplicationsState} which is used to load the app name and icon. */ setAppState( ApplicationsState applicationsState)268 public ApplicationActionButtonsPreferenceController setAppState( 269 ApplicationsState applicationsState) { 270 mApplicationsState = applicationsState; 271 return this; 272 } 273 274 /** 275 * Set the packageName, which is used to perform actions on a particular package. 276 */ setPackageName(String packageName)277 public ApplicationActionButtonsPreferenceController setPackageName(String packageName) { 278 mPackageName = packageName; 279 return this; 280 } 281 282 @Override checkInitialized()283 protected void checkInitialized() { 284 if (mAppEntry == null || mApplicationsState == null || mPackageName == null) { 285 throw new IllegalStateException( 286 "AppEntry, AppState, and PackageName should be set before calling this " 287 + "function"); 288 } 289 } 290 291 @Override onCreateInternal()292 protected void onCreateInternal() { 293 ConfirmationDialogFragment.resetListeners( 294 (ConfirmationDialogFragment) getFragmentController().findDialogByTag( 295 DISABLE_CONFIRM_DIALOG_TAG), 296 mDisableConfirmListener, 297 /* rejectListener= */ null, 298 /* neutralListener= */ null); 299 ConfirmationDialogFragment.resetListeners( 300 (ConfirmationDialogFragment) getFragmentController().findDialogByTag( 301 FORCE_STOP_CONFIRM_DIALOG_TAG), 302 mForceStopConfirmListener, 303 /* rejectListener= */ null, 304 /* neutralListener= */ null); 305 getPreference().getButton(ActionButtons.BUTTON2) 306 .setText(R.string.force_stop) 307 .setIcon(R.drawable.ic_warning) 308 .setOnClickListener(mForceStopClickListener) 309 .setEnabled(false); 310 mSession = mApplicationsState.newSession(mApplicationStateCallbacks); 311 } 312 313 @Override onStartInternal()314 protected void onStartInternal() { 315 mSession.onResume(); 316 } 317 318 @Override onStopInternal()319 protected void onStopInternal() { 320 mSession.onPause(); 321 } 322 323 @Override updateState(ActionButtonsPreference preference)324 protected void updateState(ActionButtonsPreference preference) { 325 refreshAppEntry(); 326 if (mAppEntry == null) { 327 getFragmentController().goBack(); 328 return; 329 } 330 updateForceStopButton(); 331 updateUninstallButton(); 332 } 333 refreshAppEntry()334 private void refreshAppEntry() { 335 mAppEntry = mApplicationsState.getEntry(mPackageName, UserHandle.myUserId()); 336 if (mAppEntry != null) { 337 try { 338 mPackageInfo = mPm.getPackageInfo(mPackageName, 339 PackageManager.MATCH_DISABLED_COMPONENTS | PackageManager.MATCH_ANY_USER 340 | PackageManager.GET_SIGNATURES | PackageManager.GET_PERMISSIONS); 341 } catch (PackageManager.NameNotFoundException e) { 342 LOG.e("Exception when retrieving package:" + mPackageName, e); 343 mPackageInfo = null; 344 } 345 } else { 346 mPackageInfo = null; 347 } 348 } 349 updateForceStopButton()350 private void updateForceStopButton() { 351 if (mDpm.packageHasActiveAdmins(mPackageName)) { 352 updateForceStopButtonInner(/* enabled= */ false); 353 } else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_STOPPED) == 0) { 354 // If the app isn't explicitly stopped, then always show the force stop button. 355 updateForceStopButtonInner(/* enabled= */ true); 356 } else { 357 Intent intent = new Intent(Intent.ACTION_QUERY_PACKAGE_RESTART, 358 Uri.fromParts("package", mPackageName, /* fragment= */ null)); 359 intent.putExtra(Intent.EXTRA_PACKAGES, new String[]{mPackageName}); 360 intent.putExtra(Intent.EXTRA_UID, mAppEntry.info.uid); 361 intent.putExtra(Intent.EXTRA_USER_HANDLE, 362 UserHandle.getUserId(mAppEntry.info.uid)); 363 LOG.d("Sending broadcast to query restart status for " + mPackageName); 364 getContext().sendOrderedBroadcastAsUser(intent, 365 UserHandle.CURRENT, 366 android.Manifest.permission.HANDLE_QUERY_PACKAGE_RESTART, 367 mCheckKillProcessesReceiver, 368 /* scheduler= */ null, 369 Activity.RESULT_CANCELED, 370 /* initialData= */ null, 371 /* initialExtras= */ null); 372 } 373 } 374 updateForceStopButtonInner(boolean enabled)375 private void updateForceStopButtonInner(boolean enabled) { 376 if (enabled) { 377 Boolean shouldDisable = shouldDisableButtonBecauseOfUserRestriction("Force Stop", 378 UserManager.DISALLOW_APPS_CONTROL); 379 if (shouldDisable != null) { 380 if (shouldDisable) { 381 enabled = false; 382 } else { 383 mRestriction = UserManager.DISALLOW_APPS_CONTROL; 384 } 385 } 386 } 387 388 getPreference().getButton(ActionButtons.BUTTON2).setEnabled(enabled); 389 } 390 updateUninstallButtonInner(boolean isAppEnabled)391 private void updateUninstallButtonInner(boolean isAppEnabled) { 392 ActionButtonInfo uninstallButton = getPreference().getButton(ActionButtons.BUTTON1); 393 if (isBundledApp()) { 394 if (isAppEnabled) { 395 uninstallButton.setText(R.string.disable_text).setIcon( 396 R.drawable.ic_block).setOnClickListener(mDisableClickListener); 397 } else { 398 uninstallButton.setText(R.string.enable_text).setIcon( 399 R.drawable.ic_check_circle).setOnClickListener(mEnableClickListener); 400 } 401 } else { 402 uninstallButton.setText(R.string.uninstall_text).setIcon( 403 R.drawable.ic_delete).setOnClickListener(mUninstallClickListener); 404 } 405 406 uninstallButton.setEnabled(!shouldDisableUninstallButton()); 407 } 408 updateUninstallButton()409 private void updateUninstallButton() { 410 updateUninstallButtonInner(isAppEnabled()); 411 } 412 shouldDisableUninstallButton()413 private boolean shouldDisableUninstallButton() { 414 if (shouldDisableUninstallForHomeApp()) { 415 LOG.d("Uninstall disabled for home app"); 416 return true; 417 } 418 419 if (isAppEnabled() && isKeepEnabledPackage(getContext(), mPackageName)) { 420 LOG.d("Disable button disabled for keep enabled package"); 421 return true; 422 } 423 424 if (Utils.isSystemPackage(getContext().getResources(), mPm, mPackageInfo)) { 425 LOG.d("Uninstall disabled for system package"); 426 return true; 427 } 428 429 // We don't allow uninstalling profile/device owner on any profile because if it's a system 430 // app, "uninstall" is actually "downgrade to the system version + disable", and 431 // "downgrade" will clear data on all profiles. 432 if (isProfileOrDeviceOwner(mPackageName, mDpm, mProfileHelper)) { 433 LOG.d("Uninstall disabled because package is profile or device owner"); 434 return true; 435 } 436 437 if (mDpm.isUninstallInQueue(mPackageName)) { 438 LOG.d("Uninstall disabled because intent is already queued"); 439 return true; 440 } 441 442 Boolean shouldDisable = shouldDisableButtonBecauseOfUserRestriction("Uninstall", 443 UserManager.DISALLOW_APPS_CONTROL); 444 if (shouldDisable != null) return shouldDisable; 445 446 shouldDisable = shouldDisableButtonBecauseOfUserRestriction("Uninstall", 447 UserManager.DISALLOW_UNINSTALL_APPS); 448 if (shouldDisable != null) return shouldDisable; 449 450 return false; 451 } 452 453 /** 454 * Checks whether a button should be disabled because the user has the given restriction 455 * (and whether the restriction was was set by a device admin). 456 * 457 * @param button action name (for logging purposes) 458 * @param restriction user restriction 459 * 460 * @return {@code null} if the user doesn't have the restriction, {@value Boolean#TRUE} if it 461 * should be disabled because of {@link UserManager} restrictions, or {@value Boolean#FALSE} if 462 * should not be disabled because of {@link DevicePolicyManager} restrictions (in which case 463 * {@link #mRestriction} is updated with the restriction). 464 */ 465 @Nullable shouldDisableButtonBecauseOfUserRestriction(String button, String restriction)466 private Boolean shouldDisableButtonBecauseOfUserRestriction(String button, String restriction) { 467 if (!mUserManager.hasUserRestriction(restriction)) return null; 468 469 UserHandle user = UserHandle.getUserHandleForUid(mAppEntry.info.uid); 470 471 if (mUserManager.hasBaseUserRestriction(restriction, user)) { 472 LOG.d(button + " disabled because " + user + " has " + restriction + " restriction"); 473 return Boolean.TRUE; 474 } 475 476 LOG.d(button + " NOT disabled because " + user + " has " + restriction + " restriction but " 477 + "it was set by a device admin (it will show a dialog explaining that instead)"); 478 mRestriction = restriction; 479 return Boolean.FALSE; 480 } 481 482 /** 483 * Returns {@code true} if the package is a Home app that should not be uninstalled. We don't 484 * risk downgrading bundled home apps because that can interfere with home-key resolution. We 485 * can't allow removal of the only home app, and we don't want to allow removal of an 486 * explicitly preferred home app. The user can go to Home settings and pick a different app, 487 * after which we'll permit removal of the now-not-default app. 488 */ shouldDisableUninstallForHomeApp()489 private boolean shouldDisableUninstallForHomeApp() { 490 Set<String> homePackages = new ArraySet<>(); 491 // Get list of "home" apps and trace through any meta-data references. 492 List<ResolveInfo> homeActivities = new ArrayList<>(); 493 ComponentName currentDefaultHome = mPm.getHomeActivities(homeActivities); 494 for (int i = 0; i < homeActivities.size(); i++) { 495 ResolveInfo ri = homeActivities.get(i); 496 String activityPkg = ri.activityInfo.packageName; 497 homePackages.add(activityPkg); 498 499 // Also make sure to include anything proxying for the home app. 500 Bundle metadata = ri.activityInfo.metaData; 501 if (metadata != null) { 502 String metaPkg = metadata.getString(ActivityManager.META_HOME_ALTERNATE); 503 if (signaturesMatch(metaPkg, activityPkg)) { 504 homePackages.add(metaPkg); 505 } 506 } 507 } 508 509 if (homePackages.contains(mPackageName)) { 510 if (isBundledApp()) { 511 // Don't risk a downgrade. 512 return true; 513 } else if (currentDefaultHome == null) { 514 // No preferred default. Permit uninstall only when there is more than one 515 // candidate. 516 return (homePackages.size() == 1); 517 } else { 518 // Explicit default home app. Forbid uninstall of that one, but permit it for 519 // installed-but-inactive ones. 520 return mPackageName.equals(currentDefaultHome.getPackageName()); 521 } 522 } else { 523 // Not a home app. 524 return false; 525 } 526 } 527 signaturesMatch(String pkg1, String pkg2)528 private boolean signaturesMatch(String pkg1, String pkg2) { 529 if (pkg1 != null && pkg2 != null) { 530 try { 531 int match = mPm.checkSignatures(pkg1, pkg2); 532 if (match >= PackageManager.SIGNATURE_MATCH) { 533 return true; 534 } 535 } catch (Exception e) { 536 // e.g. package not found during lookup. Possibly bad input. 537 // Just return false as this isn't a reason to crash given the use case. 538 } 539 } 540 return false; 541 } 542 isBundledApp()543 private boolean isBundledApp() { 544 return (mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0; 545 } 546 isAppEnabled()547 private boolean isAppEnabled() { 548 return mAppEntry.info.enabled && !(mAppEntry.info.enabledSetting 549 == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED); 550 } 551 552 @Override processActivityResult(int requestCode, int resultCode, @Nullable Intent data)553 public void processActivityResult(int requestCode, int resultCode, @Nullable Intent data) { 554 if (requestCode == UNINSTALL_REQUEST_CODE 555 || requestCode == UNINSTALL_DEVICE_ADMIN_REQUEST_CODE) { 556 if (resultCode == RESULT_OK) { 557 getFragmentController().goBack(); 558 } else if (resultCode == RESULT_FIRST_USER) { 559 showUninstallBlockedByAdminDialog(); 560 LOG.e("Uninstall failed"); 561 } 562 } 563 } 564 showUninstallBlockedByAdminDialog()565 private void showUninstallBlockedByAdminDialog() { 566 getFragmentController().showDialog( 567 EnterpriseUtils.getActionDisabledByAdminDialog(getContext(), 568 BLOCKED_UNINSTALL_APP, mPackageName), 569 DISABLED_BY_ADMIN_CONFIRM_DIALOG_TAG); 570 } 571 ignoreActionBecauseItsDisabledByAdmin(List<String> restrictions)572 private boolean ignoreActionBecauseItsDisabledByAdmin(List<String> restrictions) { 573 if (mRestriction == null || !restrictions.contains(mRestriction)) return false; 574 575 LOG.d("Ignoring action because of " + mRestriction); 576 getFragmentController().showDialog(ActionDisabledByAdminDialogFragment.newInstance( 577 mRestriction, UserHandle.myUserId()), DISABLED_BY_ADMIN_CONFIRM_DIALOG_TAG); 578 return true; 579 } 580 } 581