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