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