1 /* 2 * Copyright (C) 2016 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.settingslib; 18 19 import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NONE; 20 import static android.app.admin.DevicePolicyManager.PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER; 21 22 import android.annotation.NonNull; 23 import android.annotation.Nullable; 24 import android.annotation.UserIdInt; 25 import android.app.AppGlobals; 26 import android.app.admin.DevicePolicyManager; 27 import android.content.ComponentName; 28 import android.content.Context; 29 import android.content.Intent; 30 import android.content.pm.IPackageManager; 31 import android.content.pm.PackageManager; 32 import android.content.pm.UserInfo; 33 import android.graphics.drawable.Drawable; 34 import android.os.RemoteException; 35 import android.os.UserHandle; 36 import android.os.UserManager; 37 import android.provider.Settings; 38 import android.support.annotation.VisibleForTesting; 39 import android.text.SpannableStringBuilder; 40 import android.text.Spanned; 41 import android.text.style.ForegroundColorSpan; 42 import android.text.style.ImageSpan; 43 import android.view.MenuItem; 44 import android.widget.TextView; 45 46 import com.android.internal.widget.LockPatternUtils; 47 48 import java.util.List; 49 import java.util.Objects; 50 51 /** 52 * Utility class to host methods usable in adding a restricted padlock icon and showing admin 53 * support message dialog. 54 */ 55 public class RestrictedLockUtils { 56 /** 57 * @return drawables for displaying with settings that are locked by a device admin. 58 */ 59 public static Drawable getRestrictedPadlock(Context context) { 60 Drawable restrictedPadlock = context.getDrawable(R.drawable.ic_info); 61 final int iconSize = context.getResources().getDimensionPixelSize( 62 R.dimen.restricted_icon_size); 63 restrictedPadlock.setBounds(0, 0, iconSize, iconSize); 64 return restrictedPadlock; 65 } 66 67 /** 68 * Checks if a restriction is enforced on a user and returns the enforced admin and 69 * admin userId. 70 * 71 * @param userRestriction Restriction to check 72 * @param userId User which we need to check if restriction is enforced on. 73 * @return EnforcedAdmin Object containing the enforced admin component and admin user details, 74 * or {@code null} If the restriction is not set. If the restriction is set by both device owner 75 * and profile owner, then the admin component will be set to {@code null} and userId to 76 * {@link UserHandle#USER_NULL}. 77 */ 78 public static EnforcedAdmin checkIfRestrictionEnforced(Context context, 79 String userRestriction, int userId) { 80 final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService( 81 Context.DEVICE_POLICY_SERVICE); 82 if (dpm == null) { 83 return null; 84 } 85 86 final UserManager um = UserManager.get(context); 87 final List<UserManager.EnforcingUser> enforcingUsers = 88 um.getUserRestrictionSources(userRestriction, UserHandle.of(userId)); 89 90 if (enforcingUsers.isEmpty()) { 91 // Restriction is not enforced. 92 return null; 93 } else if (enforcingUsers.size() > 1) { 94 return EnforcedAdmin.createDefaultEnforcedAdminWithRestriction(userRestriction); 95 } 96 97 final int restrictionSource = enforcingUsers.get(0).getUserRestrictionSource(); 98 final int adminUserId = enforcingUsers.get(0).getUserHandle().getIdentifier(); 99 if (restrictionSource == UserManager.RESTRICTION_SOURCE_PROFILE_OWNER) { 100 // Check if it is a profile owner of the user under consideration. 101 if (adminUserId == userId) { 102 return getProfileOwner(context, userRestriction, adminUserId); 103 } else { 104 // Check if it is a profile owner of a managed profile of the current user. 105 // Otherwise it is in a separate user and we return a default EnforcedAdmin. 106 final UserInfo parentUser = um.getProfileParent(adminUserId); 107 return (parentUser != null && parentUser.id == userId) 108 ? getProfileOwner(context, userRestriction, adminUserId) 109 : EnforcedAdmin.createDefaultEnforcedAdminWithRestriction(userRestriction); 110 } 111 } else if (restrictionSource == UserManager.RESTRICTION_SOURCE_DEVICE_OWNER) { 112 // When the restriction is enforced by device owner, return the device owner admin only 113 // if the admin is for the {@param userId} otherwise return a default EnforcedAdmin. 114 return adminUserId == userId 115 ? getDeviceOwner(context, userRestriction) 116 : EnforcedAdmin.createDefaultEnforcedAdminWithRestriction(userRestriction); 117 } 118 119 // If the restriction is enforced by system then return null. 120 return null; 121 } 122 123 public static boolean hasBaseUserRestriction(Context context, 124 String userRestriction, int userId) { 125 final UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE); 126 return um.hasBaseUserRestriction(userRestriction, UserHandle.of(userId)); 127 } 128 129 /** 130 * Checks whether keyguard features are disabled by policy. 131 * 132 * @param context {@link Context} for the calling user. 133 * 134 * @param keyguardFeatures Any one of keyguard features that can be 135 * disabled by {@link android.app.admin.DevicePolicyManager#setKeyguardDisabledFeatures}. 136 * 137 * @param userId User to check enforced admin status for. 138 * 139 * @return EnforcedAdmin Object containing the enforced admin component and admin user details, 140 * or {@code null} If the notification features are not disabled. If the restriction is set by 141 * multiple admins, then the admin component will be set to {@code null} and userId to 142 * {@link UserHandle#USER_NULL}. 143 */ 144 public static EnforcedAdmin checkIfKeyguardFeaturesDisabled(Context context, 145 int keyguardFeatures, final @UserIdInt int userId) { 146 final LockSettingCheck check = (dpm, admin, checkUser) -> { 147 int effectiveFeatures = dpm.getKeyguardDisabledFeatures(admin, checkUser); 148 if (checkUser != userId) { 149 effectiveFeatures &= PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER; 150 } 151 return (effectiveFeatures & keyguardFeatures) != KEYGUARD_DISABLE_FEATURES_NONE; 152 }; 153 if (UserManager.get(context).getUserInfo(userId).isManagedProfile()) { 154 DevicePolicyManager dpm = 155 (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE); 156 return findEnforcedAdmin(dpm.getActiveAdminsAsUser(userId), dpm, userId, check); 157 } 158 return checkForLockSetting(context, userId, check); 159 } 160 161 /** 162 * Filter a set of device admins based on a predicate {@code check}. This is equivalent to 163 * {@code admins.stream().filter(check).map(x new EnforcedAdmin(admin, userId)} except it's 164 * returning a zero/one/many-type thing. 165 * 166 * @param admins set of candidate device admins identified by {@link ComponentName}. 167 * @param userId user to create the resultant {@link EnforcedAdmin} as. 168 * @param check filter predicate. 169 * 170 * @return {@code null} if none of the {@param admins} match. 171 * An {@link EnforcedAdmin} if exactly one of the admins matches. 172 * Otherwise, {@link EnforcedAdmin#MULTIPLE_ENFORCED_ADMIN} for multiple matches. 173 */ 174 @Nullable 175 private static EnforcedAdmin findEnforcedAdmin(@Nullable List<ComponentName> admins, 176 @NonNull DevicePolicyManager dpm, @UserIdInt int userId, 177 @NonNull LockSettingCheck check) { 178 if (admins == null) { 179 return null; 180 } 181 EnforcedAdmin enforcedAdmin = null; 182 for (ComponentName admin : admins) { 183 if (check.isEnforcing(dpm, admin, userId)) { 184 if (enforcedAdmin == null) { 185 enforcedAdmin = new EnforcedAdmin(admin, userId); 186 } else { 187 return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN; 188 } 189 } 190 } 191 return enforcedAdmin; 192 } 193 194 public static EnforcedAdmin checkIfUninstallBlocked(Context context, 195 String packageName, int userId) { 196 EnforcedAdmin allAppsControlDisallowedAdmin = checkIfRestrictionEnforced(context, 197 UserManager.DISALLOW_APPS_CONTROL, userId); 198 if (allAppsControlDisallowedAdmin != null) { 199 return allAppsControlDisallowedAdmin; 200 } 201 EnforcedAdmin allAppsUninstallDisallowedAdmin = checkIfRestrictionEnforced(context, 202 UserManager.DISALLOW_UNINSTALL_APPS, userId); 203 if (allAppsUninstallDisallowedAdmin != null) { 204 return allAppsUninstallDisallowedAdmin; 205 } 206 IPackageManager ipm = AppGlobals.getPackageManager(); 207 try { 208 if (ipm.getBlockUninstallForUser(packageName, userId)) { 209 return getProfileOrDeviceOwner(context, userId); 210 } 211 } catch (RemoteException e) { 212 // Nothing to do 213 } 214 return null; 215 } 216 217 /** 218 * Check if an application is suspended. 219 * 220 * @return EnforcedAdmin Object containing the enforced admin component and admin user details, 221 * or {@code null} if the application is not suspended. 222 */ 223 public static EnforcedAdmin checkIfApplicationIsSuspended(Context context, String packageName, 224 int userId) { 225 IPackageManager ipm = AppGlobals.getPackageManager(); 226 try { 227 if (ipm.isPackageSuspendedForUser(packageName, userId)) { 228 return getProfileOrDeviceOwner(context, userId); 229 } 230 } catch (RemoteException | IllegalArgumentException e) { 231 // Nothing to do 232 } 233 return null; 234 } 235 236 public static EnforcedAdmin checkIfInputMethodDisallowed(Context context, 237 String packageName, int userId) { 238 DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService( 239 Context.DEVICE_POLICY_SERVICE); 240 if (dpm == null) { 241 return null; 242 } 243 EnforcedAdmin admin = getProfileOrDeviceOwner(context, userId); 244 boolean permitted = true; 245 if (admin != null) { 246 permitted = dpm.isInputMethodPermittedByAdmin(admin.component, 247 packageName, userId); 248 } 249 int managedProfileId = getManagedProfileId(context, userId); 250 EnforcedAdmin profileAdmin = getProfileOrDeviceOwner(context, managedProfileId); 251 boolean permittedByProfileAdmin = true; 252 if (profileAdmin != null) { 253 permittedByProfileAdmin = dpm.isInputMethodPermittedByAdmin(profileAdmin.component, 254 packageName, managedProfileId); 255 } 256 if (!permitted && !permittedByProfileAdmin) { 257 return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN; 258 } else if (!permitted) { 259 return admin; 260 } else if (!permittedByProfileAdmin) { 261 return profileAdmin; 262 } 263 return null; 264 } 265 266 /** 267 * @param context 268 * @param userId user id of a managed profile. 269 * @return is remote contacts search disallowed. 270 */ 271 public static EnforcedAdmin checkIfRemoteContactSearchDisallowed(Context context, int userId) { 272 DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService( 273 Context.DEVICE_POLICY_SERVICE); 274 if (dpm == null) { 275 return null; 276 } 277 EnforcedAdmin admin = getProfileOwner(context, userId); 278 if (admin == null) { 279 return null; 280 } 281 UserHandle userHandle = UserHandle.of(userId); 282 if (dpm.getCrossProfileContactsSearchDisabled(userHandle) 283 && dpm.getCrossProfileCallerIdDisabled(userHandle)) { 284 return admin; 285 } 286 return null; 287 } 288 289 public static EnforcedAdmin checkIfAccessibilityServiceDisallowed(Context context, 290 String packageName, int userId) { 291 DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService( 292 Context.DEVICE_POLICY_SERVICE); 293 if (dpm == null) { 294 return null; 295 } 296 EnforcedAdmin admin = getProfileOrDeviceOwner(context, userId); 297 boolean permitted = true; 298 if (admin != null) { 299 permitted = dpm.isAccessibilityServicePermittedByAdmin(admin.component, 300 packageName, userId); 301 } 302 int managedProfileId = getManagedProfileId(context, userId); 303 EnforcedAdmin profileAdmin = getProfileOrDeviceOwner(context, managedProfileId); 304 boolean permittedByProfileAdmin = true; 305 if (profileAdmin != null) { 306 permittedByProfileAdmin = dpm.isAccessibilityServicePermittedByAdmin( 307 profileAdmin.component, packageName, managedProfileId); 308 } 309 if (!permitted && !permittedByProfileAdmin) { 310 return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN; 311 } else if (!permitted) { 312 return admin; 313 } else if (!permittedByProfileAdmin) { 314 return profileAdmin; 315 } 316 return null; 317 } 318 319 private static int getManagedProfileId(Context context, int userId) { 320 UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE); 321 List<UserInfo> userProfiles = um.getProfiles(userId); 322 for (UserInfo uInfo : userProfiles) { 323 if (uInfo.id == userId) { 324 continue; 325 } 326 if (uInfo.isManagedProfile()) { 327 return uInfo.id; 328 } 329 } 330 return UserHandle.USER_NULL; 331 } 332 333 /** 334 * Check if account management for a specific type of account is disabled by admin. 335 * Only a profile or device owner can disable account management. So, we check if account 336 * management is disabled and return profile or device owner on the calling user. 337 * 338 * @return EnforcedAdmin Object containing the enforced admin component and admin user details, 339 * or {@code null} if the account management is not disabled. 340 */ 341 public static EnforcedAdmin checkIfAccountManagementDisabled(Context context, 342 String accountType, int userId) { 343 if (accountType == null) { 344 return null; 345 } 346 DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService( 347 Context.DEVICE_POLICY_SERVICE); 348 PackageManager pm = context.getPackageManager(); 349 if (!pm.hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN) || dpm == null) { 350 return null; 351 } 352 boolean isAccountTypeDisabled = false; 353 String[] disabledTypes = dpm.getAccountTypesWithManagementDisabledAsUser(userId); 354 for (String type : disabledTypes) { 355 if (accountType.equals(type)) { 356 isAccountTypeDisabled = true; 357 break; 358 } 359 } 360 if (!isAccountTypeDisabled) { 361 return null; 362 } 363 return getProfileOrDeviceOwner(context, userId); 364 } 365 366 /** 367 * Check if {@param packageName} is restricted by the profile or device owner from using 368 * metered data. 369 * 370 * @return EnforcedAdmin object containing the enforced admin component and admin user details, 371 * or {@code null} if the {@param packageName} is not restricted. 372 */ 373 public static EnforcedAdmin checkIfMeteredDataRestricted(Context context, 374 String packageName, int userId) { 375 final EnforcedAdmin enforcedAdmin = getProfileOrDeviceOwner(context, userId); 376 if (enforcedAdmin == null) { 377 return null; 378 } 379 380 final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService( 381 Context.DEVICE_POLICY_SERVICE); 382 return dpm.isMeteredDataDisabledPackageForUser(enforcedAdmin.component, packageName, userId) 383 ? enforcedAdmin : null; 384 } 385 386 /** 387 * Checks if {@link android.app.admin.DevicePolicyManager#setAutoTimeRequired} is enforced 388 * on the device. 389 * 390 * @return EnforcedAdmin Object containing the device owner component and 391 * userId the device owner is running as, or {@code null} setAutoTimeRequired is not enforced. 392 */ 393 public static EnforcedAdmin checkIfAutoTimeRequired(Context context) { 394 DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService( 395 Context.DEVICE_POLICY_SERVICE); 396 if (dpm == null || !dpm.getAutoTimeRequired()) { 397 return null; 398 } 399 ComponentName adminComponent = dpm.getDeviceOwnerComponentOnCallingUser(); 400 return new EnforcedAdmin(adminComponent, UserHandle.myUserId()); 401 } 402 403 /** 404 * Checks if an admin has enforced minimum password quality requirements on the given user. 405 * 406 * @return EnforcedAdmin Object containing the enforced admin component and admin user details, 407 * or {@code null} if no quality requirements are set. If the requirements are set by 408 * multiple device admins, then the admin component will be set to {@code null} and userId to 409 * {@link UserHandle#USER_NULL}. 410 */ 411 public static EnforcedAdmin checkIfPasswordQualityIsSet(Context context, int userId) { 412 final LockSettingCheck check = 413 (DevicePolicyManager dpm, ComponentName admin, @UserIdInt int checkUser) -> 414 dpm.getPasswordQuality(admin, checkUser) 415 > DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; 416 417 final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService( 418 Context.DEVICE_POLICY_SERVICE); 419 if (dpm == null) { 420 return null; 421 } 422 423 LockPatternUtils lockPatternUtils = new LockPatternUtils(context); 424 if (sProxy.isSeparateProfileChallengeEnabled(lockPatternUtils, userId)) { 425 // userId is managed profile and has a separate challenge, only consider 426 // the admins in that user. 427 final List<ComponentName> admins = dpm.getActiveAdminsAsUser(userId); 428 if (admins == null) { 429 return null; 430 } 431 EnforcedAdmin enforcedAdmin = null; 432 for (ComponentName admin : admins) { 433 if (check.isEnforcing(dpm, admin, userId)) { 434 if (enforcedAdmin == null) { 435 enforcedAdmin = new EnforcedAdmin(admin, userId); 436 } else { 437 return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN; 438 } 439 } 440 } 441 return enforcedAdmin; 442 } else { 443 return checkForLockSetting(context, userId, check); 444 } 445 } 446 447 /** 448 * Checks if any admin has set maximum time to lock. 449 * 450 * @return EnforcedAdmin Object containing the enforced admin component and admin user details, 451 * or {@code null} if no admin has set this restriction. If multiple admins has set this, then 452 * the admin component will be set to {@code null} and userId to {@link UserHandle#USER_NULL} 453 */ 454 public static EnforcedAdmin checkIfMaximumTimeToLockIsSet(Context context) { 455 return checkForLockSetting(context, UserHandle.myUserId(), 456 (DevicePolicyManager dpm, ComponentName admin, @UserIdInt int userId) -> 457 dpm.getMaximumTimeToLock(admin, userId) > 0); 458 } 459 460 private interface LockSettingCheck { 461 boolean isEnforcing(DevicePolicyManager dpm, ComponentName admin, @UserIdInt int userId); 462 } 463 464 /** 465 * Checks whether any of the user's profiles enforce the lock setting. A managed profile is only 466 * included if it does not have a separate challenge. 467 * 468 * The user identified by {@param userId} is always included. 469 */ 470 private static EnforcedAdmin checkForLockSetting( 471 Context context, @UserIdInt int userId, LockSettingCheck check) { 472 final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService( 473 Context.DEVICE_POLICY_SERVICE); 474 if (dpm == null) { 475 return null; 476 } 477 final LockPatternUtils lockPatternUtils = new LockPatternUtils(context); 478 EnforcedAdmin enforcedAdmin = null; 479 // Return all admins for this user and the profiles that are visible from this 480 // user that do not use a separate work challenge. 481 for (UserInfo userInfo : UserManager.get(context).getProfiles(userId)) { 482 final List<ComponentName> admins = dpm.getActiveAdminsAsUser(userInfo.id); 483 if (admins == null) { 484 continue; 485 } 486 final boolean isSeparateProfileChallengeEnabled = 487 sProxy.isSeparateProfileChallengeEnabled(lockPatternUtils, userInfo.id); 488 for (ComponentName admin : admins) { 489 if (!isSeparateProfileChallengeEnabled) { 490 if (check.isEnforcing(dpm, admin, userInfo.id)) { 491 if (enforcedAdmin == null) { 492 enforcedAdmin = new EnforcedAdmin(admin, userInfo.id); 493 } else { 494 return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN; 495 } 496 // This same admins could have set policies both on the managed profile 497 // and on the parent. So, if the admin has set the policy on the 498 // managed profile here, we don't need to further check if that admin 499 // has set policy on the parent admin. 500 continue; 501 } 502 } 503 if (userInfo.isManagedProfile()) { 504 // If userInfo.id is a managed profile, we also need to look at 505 // the policies set on the parent. 506 DevicePolicyManager parentDpm = sProxy.getParentProfileInstance(dpm, userInfo); 507 if (check.isEnforcing(parentDpm, admin, userInfo.id)) { 508 if (enforcedAdmin == null) { 509 enforcedAdmin = new EnforcedAdmin(admin, userInfo.id); 510 } else { 511 return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN; 512 } 513 } 514 } 515 } 516 } 517 return enforcedAdmin; 518 } 519 520 public static EnforcedAdmin getProfileOrDeviceOwner(Context context, int userId) { 521 return getProfileOrDeviceOwner(context, null, userId); 522 } 523 524 public static EnforcedAdmin getProfileOrDeviceOwner( 525 Context context, String enforcedRestriction, int userId) { 526 if (userId == UserHandle.USER_NULL) { 527 return null; 528 } 529 final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService( 530 Context.DEVICE_POLICY_SERVICE); 531 if (dpm == null) { 532 return null; 533 } 534 ComponentName adminComponent = dpm.getProfileOwnerAsUser(userId); 535 if (adminComponent != null) { 536 return new EnforcedAdmin(adminComponent, enforcedRestriction, userId); 537 } 538 if (dpm.getDeviceOwnerUserId() == userId) { 539 adminComponent = dpm.getDeviceOwnerComponentOnAnyUser(); 540 if (adminComponent != null) { 541 return new EnforcedAdmin(adminComponent, enforcedRestriction, userId); 542 } 543 } 544 return null; 545 } 546 547 public static EnforcedAdmin getDeviceOwner(Context context) { 548 return getDeviceOwner(context, null); 549 } 550 551 private static EnforcedAdmin getDeviceOwner(Context context, String enforcedRestriction) { 552 final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService( 553 Context.DEVICE_POLICY_SERVICE); 554 if (dpm == null) { 555 return null; 556 } 557 ComponentName adminComponent = dpm.getDeviceOwnerComponentOnAnyUser(); 558 if (adminComponent != null) { 559 return new EnforcedAdmin( 560 adminComponent, enforcedRestriction, dpm.getDeviceOwnerUserId()); 561 } 562 return null; 563 } 564 565 private static EnforcedAdmin getProfileOwner(Context context, int userId) { 566 return getProfileOwner(context, null, userId); 567 } 568 569 private static EnforcedAdmin getProfileOwner( 570 Context context, String enforcedRestriction, int userId) { 571 if (userId == UserHandle.USER_NULL) { 572 return null; 573 } 574 final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService( 575 Context.DEVICE_POLICY_SERVICE); 576 if (dpm == null) { 577 return null; 578 } 579 ComponentName adminComponent = dpm.getProfileOwnerAsUser(userId); 580 if (adminComponent != null) { 581 return new EnforcedAdmin(adminComponent, enforcedRestriction, userId); 582 } 583 return null; 584 } 585 586 /** 587 * Set the menu item as disabled by admin by adding a restricted padlock at the end of the 588 * text and set the click listener which will send an intent to show the admin support details 589 * dialog. If the admin is null, remove the padlock and disabled color span. When the admin is 590 * null, we also set the OnMenuItemClickListener to null, so if you want to set a custom 591 * OnMenuItemClickListener, set it after calling this method. 592 */ 593 public static void setMenuItemAsDisabledByAdmin(final Context context, 594 final MenuItem item, final EnforcedAdmin admin) { 595 SpannableStringBuilder sb = new SpannableStringBuilder(item.getTitle()); 596 removeExistingRestrictedSpans(sb); 597 598 if (admin != null) { 599 final int disabledColor = context.getColor(R.color.disabled_text_color); 600 sb.setSpan(new ForegroundColorSpan(disabledColor), 0, sb.length(), 601 Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 602 ImageSpan image = new RestrictedLockImageSpan(context); 603 sb.append(" ", image, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 604 605 item.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { 606 @Override 607 public boolean onMenuItemClick(MenuItem item) { 608 sendShowAdminSupportDetailsIntent(context, admin); 609 return true; 610 } 611 }); 612 } else { 613 item.setOnMenuItemClickListener(null); 614 } 615 item.setTitle(sb); 616 } 617 618 private static void removeExistingRestrictedSpans(SpannableStringBuilder sb) { 619 final int length = sb.length(); 620 RestrictedLockImageSpan[] imageSpans = sb.getSpans(length - 1, length, 621 RestrictedLockImageSpan.class); 622 for (ImageSpan span : imageSpans) { 623 final int start = sb.getSpanStart(span); 624 final int end = sb.getSpanEnd(span); 625 sb.removeSpan(span); 626 sb.delete(start, end); 627 } 628 ForegroundColorSpan[] colorSpans = sb.getSpans(0, length, ForegroundColorSpan.class); 629 for (ForegroundColorSpan span : colorSpans) { 630 sb.removeSpan(span); 631 } 632 } 633 634 /** 635 * Send the intent to trigger the {@link android.settings.ShowAdminSupportDetailsDialog}. 636 */ 637 public static void sendShowAdminSupportDetailsIntent(Context context, EnforcedAdmin admin) { 638 final Intent intent = getShowAdminSupportDetailsIntent(context, admin); 639 int targetUserId = UserHandle.myUserId(); 640 if (admin != null && admin.userId != UserHandle.USER_NULL 641 && isCurrentUserOrProfile(context, admin.userId)) { 642 targetUserId = admin.userId; 643 } 644 intent.putExtra(DevicePolicyManager.EXTRA_RESTRICTION, admin.enforcedRestriction); 645 context.startActivityAsUser(intent, new UserHandle(targetUserId)); 646 } 647 648 public static Intent getShowAdminSupportDetailsIntent(Context context, EnforcedAdmin admin) { 649 final Intent intent = new Intent(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS); 650 if (admin != null) { 651 if (admin.component != null) { 652 intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, admin.component); 653 } 654 int adminUserId = UserHandle.myUserId(); 655 if (admin.userId != UserHandle.USER_NULL) { 656 adminUserId = admin.userId; 657 } 658 intent.putExtra(Intent.EXTRA_USER_ID, adminUserId); 659 } 660 return intent; 661 } 662 663 public static boolean isCurrentUserOrProfile(Context context, int userId) { 664 UserManager um = UserManager.get(context); 665 for (UserInfo userInfo : um.getProfiles(UserHandle.myUserId())) { 666 if (userInfo.id == userId) { 667 return true; 668 } 669 } 670 return false; 671 } 672 673 public static boolean isAdminInCurrentUserOrProfile(Context context, ComponentName admin) { 674 DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService( 675 Context.DEVICE_POLICY_SERVICE); 676 UserManager um = UserManager.get(context); 677 for (UserInfo userInfo : um.getProfiles(UserHandle.myUserId())) { 678 if (dpm.isAdminActiveAsUser(admin, userInfo.id)) { 679 return true; 680 } 681 } 682 return false; 683 } 684 685 public static void setTextViewPadlock(Context context, 686 TextView textView, boolean showPadlock) { 687 final SpannableStringBuilder sb = new SpannableStringBuilder(textView.getText()); 688 removeExistingRestrictedSpans(sb); 689 if (showPadlock) { 690 final ImageSpan image = new RestrictedLockImageSpan(context); 691 sb.append(" ", image, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 692 } 693 textView.setText(sb); 694 } 695 696 /** 697 * Takes a {@link android.widget.TextView} and applies an alpha so that the text looks like 698 * disabled and appends a padlock to the text. This assumes that there are no 699 * ForegroundColorSpans and RestrictedLockImageSpans used on the TextView. 700 */ 701 public static void setTextViewAsDisabledByAdmin(Context context, 702 TextView textView, boolean disabled) { 703 final SpannableStringBuilder sb = new SpannableStringBuilder(textView.getText()); 704 removeExistingRestrictedSpans(sb); 705 if (disabled) { 706 final int disabledColor = context.getColor(R.color.disabled_text_color); 707 sb.setSpan(new ForegroundColorSpan(disabledColor), 0, sb.length(), 708 Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 709 textView.setCompoundDrawables(null, null, getRestrictedPadlock(context), null); 710 textView.setCompoundDrawablePadding(context.getResources().getDimensionPixelSize( 711 R.dimen.restricted_icon_padding)); 712 } else { 713 textView.setCompoundDrawables(null, null, null, null); 714 } 715 textView.setText(sb); 716 } 717 718 public static class EnforcedAdmin { 719 @Nullable 720 public ComponentName component = null; 721 /** 722 * The restriction enforced by admin. It could be any user restriction or policy like 723 * {@link DevicePolicyManager#POLICY_DISABLE_CAMERA}. 724 */ 725 @Nullable 726 public String enforcedRestriction = null; 727 public int userId = UserHandle.USER_NULL; 728 729 // We use this to represent the case where a policy is enforced by multiple admins. 730 public final static EnforcedAdmin MULTIPLE_ENFORCED_ADMIN = new EnforcedAdmin(); 731 732 public static EnforcedAdmin createDefaultEnforcedAdminWithRestriction( 733 String enforcedRestriction) { 734 EnforcedAdmin enforcedAdmin = new EnforcedAdmin(); 735 enforcedAdmin.enforcedRestriction = enforcedRestriction; 736 return enforcedAdmin; 737 } 738 739 public EnforcedAdmin(ComponentName component, int userId) { 740 this.component = component; 741 this.userId = userId; 742 } 743 744 public EnforcedAdmin(ComponentName component, String enforcedRestriction, int userId) { 745 this.component = component; 746 this.enforcedRestriction = enforcedRestriction; 747 this.userId = userId; 748 } 749 750 public EnforcedAdmin(EnforcedAdmin other) { 751 if (other == null) { 752 throw new IllegalArgumentException(); 753 } 754 this.component = other.component; 755 this.enforcedRestriction = other.enforcedRestriction; 756 this.userId = other.userId; 757 } 758 759 public EnforcedAdmin() { 760 } 761 762 @Override 763 public boolean equals(Object o) { 764 if (this == o) return true; 765 if (o == null || getClass() != o.getClass()) return false; 766 EnforcedAdmin that = (EnforcedAdmin) o; 767 return userId == that.userId && 768 Objects.equals(component, that.component) && 769 Objects.equals(enforcedRestriction, that.enforcedRestriction); 770 } 771 772 @Override 773 public int hashCode() { 774 return Objects.hash(component, enforcedRestriction, userId); 775 } 776 777 @Override 778 public String toString() { 779 return "EnforcedAdmin{" + 780 "component=" + component + 781 ", enforcedRestriction='" + enforcedRestriction + 782 ", userId=" + userId + 783 '}'; 784 } 785 } 786 787 /** 788 * Static {@link LockPatternUtils} and {@link DevicePolicyManager} wrapper for testing purposes. 789 * {@link LockPatternUtils} is an internal API not supported by robolectric. 790 * {@link DevicePolicyManager} has a {@code getProfileParent} not yet suppored by robolectric. 791 */ 792 @VisibleForTesting 793 static Proxy sProxy = new Proxy(); 794 795 @VisibleForTesting 796 static class Proxy { 797 public boolean isSeparateProfileChallengeEnabled(LockPatternUtils utils, int userHandle) { 798 return utils.isSeparateProfileChallengeEnabled(userHandle); 799 } 800 801 public DevicePolicyManager getParentProfileInstance(DevicePolicyManager dpm, UserInfo ui) { 802 return dpm.getParentProfileInstance(ui); 803 } 804 } 805 } 806