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 271 boolean permittedByParentAdmin = true; 272 EnforcedAdmin profileAdmin = null; 273 int managedProfileId = getManagedProfileId(context, userId); 274 if (managedProfileId != UserHandle.USER_NULL) { 275 profileAdmin = getProfileOrDeviceOwner(context, getUserHandleOf(managedProfileId)); 276 // If the device is an organization-owned device with a managed profile, the 277 // managedProfileId will be used instead of the affected userId. This is because 278 // isInputMethodPermittedByAdmin is called on the parent DPM instance, which will 279 // return results affecting the personal profile. 280 if (profileAdmin != null && dpm.isOrganizationOwnedDeviceWithManagedProfile()) { 281 DevicePolicyManager parentDpm = sProxy.getParentProfileInstance(dpm, 282 UserManager.get(context).getUserInfo(managedProfileId)); 283 permittedByParentAdmin = parentDpm.isInputMethodPermittedByAdmin( 284 profileAdmin.component, packageName, managedProfileId); 285 } 286 } 287 if (!permitted && !permittedByParentAdmin) { 288 return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN; 289 } else if (!permitted) { 290 return admin; 291 } else if (!permittedByParentAdmin) { 292 return profileAdmin; 293 } 294 return null; 295 } 296 297 /** 298 * @param context 299 * @param userId user id of a managed profile. 300 * @return is remote contacts search disallowed. 301 */ checkIfRemoteContactSearchDisallowed(Context context, int userId)302 public static EnforcedAdmin checkIfRemoteContactSearchDisallowed(Context context, int userId) { 303 DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService( 304 Context.DEVICE_POLICY_SERVICE); 305 if (dpm == null) { 306 return null; 307 } 308 EnforcedAdmin admin = getProfileOwner(context, userId); 309 if (admin == null) { 310 return null; 311 } 312 UserHandle userHandle = UserHandle.of(userId); 313 if (dpm.getCrossProfileContactsSearchDisabled(userHandle) 314 && dpm.getCrossProfileCallerIdDisabled(userHandle)) { 315 return admin; 316 } 317 return null; 318 } 319 checkIfAccessibilityServiceDisallowed(Context context, String packageName, int userId)320 public static EnforcedAdmin checkIfAccessibilityServiceDisallowed(Context context, 321 String packageName, int userId) { 322 DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService( 323 Context.DEVICE_POLICY_SERVICE); 324 if (dpm == null) { 325 return null; 326 } 327 EnforcedAdmin admin = getProfileOrDeviceOwner(context, getUserHandleOf(userId)); 328 boolean permitted = true; 329 if (admin != null) { 330 permitted = dpm.isAccessibilityServicePermittedByAdmin(admin.component, 331 packageName, userId); 332 } 333 int managedProfileId = getManagedProfileId(context, userId); 334 EnforcedAdmin profileAdmin = getProfileOrDeviceOwner(context, 335 getUserHandleOf(managedProfileId)); 336 boolean permittedByProfileAdmin = true; 337 if (profileAdmin != null) { 338 permittedByProfileAdmin = dpm.isAccessibilityServicePermittedByAdmin( 339 profileAdmin.component, packageName, managedProfileId); 340 } 341 if (!permitted && !permittedByProfileAdmin) { 342 return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN; 343 } else if (!permitted) { 344 return admin; 345 } else if (!permittedByProfileAdmin) { 346 return profileAdmin; 347 } 348 return null; 349 } 350 getManagedProfileId(Context context, int userId)351 private static int getManagedProfileId(Context context, int userId) { 352 UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE); 353 List<UserInfo> userProfiles = um.getProfiles(userId); 354 for (UserInfo uInfo : userProfiles) { 355 if (uInfo.id == userId) { 356 continue; 357 } 358 if (uInfo.isManagedProfile()) { 359 return uInfo.id; 360 } 361 } 362 return UserHandle.USER_NULL; 363 } 364 365 /** 366 * Check if account management for a specific type of account is disabled by admin. 367 * Only a profile or device owner can disable account management. So, we check if account 368 * management is disabled and return profile or device owner on the calling user. 369 * 370 * @return EnforcedAdmin Object containing the enforced admin component and admin user details, 371 * or {@code null} if the account management is not disabled. 372 */ checkIfAccountManagementDisabled(Context context, String accountType, int userId)373 public static EnforcedAdmin checkIfAccountManagementDisabled(Context context, 374 String accountType, int userId) { 375 if (accountType == null) { 376 return null; 377 } 378 DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService( 379 Context.DEVICE_POLICY_SERVICE); 380 PackageManager pm = context.getPackageManager(); 381 if (!pm.hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN) || dpm == null) { 382 return null; 383 } 384 boolean isAccountTypeDisabled = false; 385 String[] disabledTypes = dpm.getAccountTypesWithManagementDisabledAsUser(userId); 386 for (String type : disabledTypes) { 387 if (accountType.equals(type)) { 388 isAccountTypeDisabled = true; 389 break; 390 } 391 } 392 if (!isAccountTypeDisabled) { 393 return null; 394 } 395 return getProfileOrDeviceOwner(context, getUserHandleOf(userId)); 396 } 397 398 /** 399 * Check if USB data signaling (except from charging functions) is disabled by the admin. 400 * Only a device owner or a profile owner on an organization-owned managed profile can disable 401 * USB data signaling. 402 * 403 * @return EnforcedAdmin Object containing the enforced admin component and admin user details, 404 * or {@code null} if USB data signaling is not disabled. 405 */ checkIfUsbDataSignalingIsDisabled(Context context, int userId)406 public static EnforcedAdmin checkIfUsbDataSignalingIsDisabled(Context context, int userId) { 407 DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class); 408 if (dpm == null || dpm.isUsbDataSignalingEnabledForUser(userId)) { 409 return null; 410 } else { 411 EnforcedAdmin admin = getProfileOrDeviceOwner(context, getUserHandleOf(userId)); 412 int managedProfileId = getManagedProfileId(context, userId); 413 if (admin == null && managedProfileId != UserHandle.USER_NULL) { 414 admin = getProfileOrDeviceOwner(context, getUserHandleOf(managedProfileId)); 415 } 416 return admin; 417 } 418 } 419 420 /** 421 * Check if {@param packageName} is restricted by the profile or device owner from using 422 * metered data. 423 * 424 * @return EnforcedAdmin object containing the enforced admin component and admin user details, 425 * or {@code null} if the {@param packageName} is not restricted. 426 */ checkIfMeteredDataRestricted(Context context, String packageName, int userId)427 public static EnforcedAdmin checkIfMeteredDataRestricted(Context context, 428 String packageName, int userId) { 429 final EnforcedAdmin enforcedAdmin = getProfileOrDeviceOwner(context, 430 getUserHandleOf(userId)); 431 if (enforcedAdmin == null) { 432 return null; 433 } 434 435 final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService( 436 Context.DEVICE_POLICY_SERVICE); 437 return dpm.isMeteredDataDisabledPackageForUser(enforcedAdmin.component, packageName, userId) 438 ? enforcedAdmin : null; 439 } 440 441 /** 442 * Checks if an admin has enforced minimum password quality or complexity requirements on the 443 * given user. 444 * 445 * @return EnforcedAdmin Object containing the enforced admin component and admin user details, 446 * or {@code null} if no quality requirements are set. If the requirements are set by 447 * multiple device admins, then the admin component will be set to {@code null} and userId to 448 * {@link UserHandle#USER_NULL}. 449 */ checkIfPasswordQualityIsSet(Context context, int userId)450 public static EnforcedAdmin checkIfPasswordQualityIsSet(Context context, int userId) { 451 final LockSettingCheck check = 452 (DevicePolicyManager dpm, ComponentName admin, @UserIdInt int checkUser) -> 453 dpm.getPasswordQuality(admin, checkUser) 454 > DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; 455 456 final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService( 457 Context.DEVICE_POLICY_SERVICE); 458 if (dpm == null) { 459 return null; 460 } 461 462 LockPatternUtils lockPatternUtils = new LockPatternUtils(context); 463 final int aggregatedComplexity = dpm.getAggregatedPasswordComplexityForUser(userId); 464 if (aggregatedComplexity > DevicePolicyManager.PASSWORD_COMPLEXITY_NONE) { 465 // First, check if there's a Device Owner. If so, then only it can apply password 466 // complexity requiremnts (there can be no secondary profiles). 467 final UserHandle deviceOwnerUser = dpm.getDeviceOwnerUser(); 468 if (deviceOwnerUser != null) { 469 return new EnforcedAdmin(dpm.getDeviceOwnerComponentOnAnyUser(), deviceOwnerUser); 470 } 471 472 // The complexity could be enforced by a Profile Owner - either in the current user 473 // or the current user is the parent user that is affected by the profile owner. 474 for (UserInfo userInfo : UserManager.get(context).getProfiles(userId)) { 475 final ComponentName profileOwnerComponent = dpm.getProfileOwnerAsUser(userInfo.id); 476 if (profileOwnerComponent != null) { 477 return new EnforcedAdmin(profileOwnerComponent, getUserHandleOf(userInfo.id)); 478 } 479 } 480 481 // Should not get here: A Device Owner or Profile Owner should be found. 482 throw new IllegalStateException( 483 String.format("Could not find admin enforcing complexity %d for user %d", 484 aggregatedComplexity, userId)); 485 } 486 487 if (sProxy.isSeparateProfileChallengeEnabled(lockPatternUtils, userId)) { 488 // userId is managed profile and has a separate challenge, only consider 489 // the admins in that user. 490 final List<ComponentName> admins = dpm.getActiveAdminsAsUser(userId); 491 if (admins == null) { 492 return null; 493 } 494 EnforcedAdmin enforcedAdmin = null; 495 final UserHandle user = getUserHandleOf(userId); 496 for (ComponentName admin : admins) { 497 if (check.isEnforcing(dpm, admin, userId)) { 498 if (enforcedAdmin == null) { 499 enforcedAdmin = new EnforcedAdmin(admin, user); 500 } else { 501 return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN; 502 } 503 } 504 } 505 return enforcedAdmin; 506 } else { 507 return checkForLockSetting(context, userId, check); 508 } 509 } 510 511 /** 512 * Checks if any admin has set maximum time to lock. 513 * 514 * @return EnforcedAdmin Object containing the enforced admin component and admin user details, 515 * or {@code null} if no admin has set this restriction. If multiple admins has set this, then 516 * the admin component will be set to {@code null} and userId to {@link UserHandle#USER_NULL} 517 */ checkIfMaximumTimeToLockIsSet(Context context)518 public static EnforcedAdmin checkIfMaximumTimeToLockIsSet(Context context) { 519 return checkForLockSetting(context, UserHandle.myUserId(), 520 (DevicePolicyManager dpm, ComponentName admin, @UserIdInt int userId) -> 521 dpm.getMaximumTimeToLock(admin, userId) > 0); 522 } 523 524 private interface LockSettingCheck { isEnforcing(DevicePolicyManager dpm, ComponentName admin, @UserIdInt int userId)525 boolean isEnforcing(DevicePolicyManager dpm, ComponentName admin, @UserIdInt int userId); 526 } 527 528 /** 529 * Checks whether any of the user's profiles enforce the lock setting. A managed profile is only 530 * included if it does not have a separate challenge. 531 * 532 * The user identified by {@param userId} is always included. 533 */ checkForLockSetting( Context context, @UserIdInt int userId, LockSettingCheck check)534 private static EnforcedAdmin checkForLockSetting( 535 Context context, @UserIdInt int userId, LockSettingCheck check) { 536 final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService( 537 Context.DEVICE_POLICY_SERVICE); 538 if (dpm == null) { 539 return null; 540 } 541 final LockPatternUtils lockPatternUtils = new LockPatternUtils(context); 542 EnforcedAdmin enforcedAdmin = null; 543 // Return all admins for this user and the profiles that are visible from this 544 // user that do not use a separate work challenge. 545 for (UserInfo userInfo : UserManager.get(context).getProfiles(userId)) { 546 final List<ComponentName> admins = dpm.getActiveAdminsAsUser(userInfo.id); 547 if (admins == null) { 548 continue; 549 } 550 final UserHandle user = getUserHandleOf(userInfo.id); 551 final boolean isSeparateProfileChallengeEnabled = 552 sProxy.isSeparateProfileChallengeEnabled(lockPatternUtils, userInfo.id); 553 for (ComponentName admin : admins) { 554 if (!isSeparateProfileChallengeEnabled) { 555 if (check.isEnforcing(dpm, admin, userInfo.id)) { 556 if (enforcedAdmin == null) { 557 enforcedAdmin = new EnforcedAdmin(admin, user); 558 } else { 559 return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN; 560 } 561 // This same admins could have set policies both on the managed profile 562 // and on the parent. So, if the admin has set the policy on the 563 // managed profile here, we don't need to further check if that admin 564 // has set policy on the parent admin. 565 continue; 566 } 567 } 568 if (userInfo.isManagedProfile()) { 569 // If userInfo.id is a managed profile, we also need to look at 570 // the policies set on the parent. 571 DevicePolicyManager parentDpm = sProxy.getParentProfileInstance(dpm, userInfo); 572 if (check.isEnforcing(parentDpm, admin, userInfo.id)) { 573 if (enforcedAdmin == null) { 574 enforcedAdmin = new EnforcedAdmin(admin, user); 575 } else { 576 return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN; 577 } 578 } 579 } 580 } 581 } 582 return enforcedAdmin; 583 } 584 getDeviceOwner(Context context)585 public static EnforcedAdmin getDeviceOwner(Context context) { 586 return getDeviceOwner(context, null); 587 } 588 getDeviceOwner(Context context, String enforcedRestriction)589 private static EnforcedAdmin getDeviceOwner(Context context, String enforcedRestriction) { 590 final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService( 591 Context.DEVICE_POLICY_SERVICE); 592 if (dpm == null) { 593 return null; 594 } 595 ComponentName adminComponent = dpm.getDeviceOwnerComponentOnAnyUser(); 596 if (adminComponent != null) { 597 return new EnforcedAdmin( 598 adminComponent, enforcedRestriction, dpm.getDeviceOwnerUser()); 599 } 600 return null; 601 } 602 getProfileOwner(Context context, int userId)603 private static EnforcedAdmin getProfileOwner(Context context, int userId) { 604 return getProfileOwner(context, null, userId); 605 } 606 getProfileOwner( Context context, String enforcedRestriction, int userId)607 private static EnforcedAdmin getProfileOwner( 608 Context context, String enforcedRestriction, int userId) { 609 if (userId == UserHandle.USER_NULL) { 610 return null; 611 } 612 final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService( 613 Context.DEVICE_POLICY_SERVICE); 614 if (dpm == null) { 615 return null; 616 } 617 ComponentName adminComponent = dpm.getProfileOwnerAsUser(userId); 618 if (adminComponent != null) { 619 return new EnforcedAdmin(adminComponent, enforcedRestriction, getUserHandleOf(userId)); 620 } 621 return null; 622 } 623 624 /** 625 * Set the menu item as disabled by admin by adding a restricted padlock at the end of the 626 * text and set the click listener which will send an intent to show the admin support details 627 * dialog. If the admin is null, remove the padlock and disabled color span. When the admin is 628 * null, we also set the OnMenuItemClickListener to null, so if you want to set a custom 629 * OnMenuItemClickListener, set it after calling this method. 630 */ setMenuItemAsDisabledByAdmin(final Context context, final MenuItem item, final EnforcedAdmin admin)631 public static void setMenuItemAsDisabledByAdmin(final Context context, 632 final MenuItem item, final EnforcedAdmin admin) { 633 SpannableStringBuilder sb = new SpannableStringBuilder(item.getTitle()); 634 removeExistingRestrictedSpans(sb); 635 636 if (admin != null) { 637 final int disabledColor = context.getColor(R.color.disabled_text_color); 638 sb.setSpan(new ForegroundColorSpan(disabledColor), 0, sb.length(), 639 Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 640 ImageSpan image = new RestrictedLockImageSpan(context); 641 sb.append(" ", image, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 642 643 item.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { 644 @Override 645 public boolean onMenuItemClick(MenuItem item) { 646 sendShowAdminSupportDetailsIntent(context, admin); 647 return true; 648 } 649 }); 650 } else { 651 item.setOnMenuItemClickListener(null); 652 } 653 item.setTitle(sb); 654 } 655 removeExistingRestrictedSpans(SpannableStringBuilder sb)656 private static void removeExistingRestrictedSpans(SpannableStringBuilder sb) { 657 final int length = sb.length(); 658 RestrictedLockImageSpan[] imageSpans = sb.getSpans(length - 1, length, 659 RestrictedLockImageSpan.class); 660 for (ImageSpan span : imageSpans) { 661 final int start = sb.getSpanStart(span); 662 final int end = sb.getSpanEnd(span); 663 sb.removeSpan(span); 664 sb.delete(start, end); 665 } 666 ForegroundColorSpan[] colorSpans = sb.getSpans(0, length, ForegroundColorSpan.class); 667 for (ForegroundColorSpan span : colorSpans) { 668 sb.removeSpan(span); 669 } 670 } 671 isAdminInCurrentUserOrProfile(Context context, ComponentName admin)672 public static boolean isAdminInCurrentUserOrProfile(Context context, ComponentName admin) { 673 DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService( 674 Context.DEVICE_POLICY_SERVICE); 675 UserManager um = UserManager.get(context); 676 for (UserInfo userInfo : um.getProfiles(UserHandle.myUserId())) { 677 if (dpm.isAdminActiveAsUser(admin, userInfo.id)) { 678 return true; 679 } 680 } 681 return false; 682 } 683 setTextViewPadlock(Context context, TextView textView, boolean showPadlock)684 public static void setTextViewPadlock(Context context, 685 TextView textView, boolean showPadlock) { 686 final SpannableStringBuilder sb = new SpannableStringBuilder(textView.getText()); 687 removeExistingRestrictedSpans(sb); 688 if (showPadlock) { 689 final ImageSpan image = new RestrictedLockImageSpan(context); 690 sb.append(" ", image, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 691 } 692 textView.setText(sb); 693 } 694 695 /** 696 * Takes a {@link android.widget.TextView} and applies an alpha so that the text looks like 697 * disabled and appends a padlock to the text. This assumes that there are no 698 * ForegroundColorSpans and RestrictedLockImageSpans used on the TextView. 699 */ setTextViewAsDisabledByAdmin(Context context, TextView textView, boolean disabled)700 public static void setTextViewAsDisabledByAdmin(Context context, 701 TextView textView, boolean disabled) { 702 final SpannableStringBuilder sb = new SpannableStringBuilder(textView.getText()); 703 removeExistingRestrictedSpans(sb); 704 if (disabled) { 705 final int disabledColor = Utils.getDisabled(context, 706 textView.getCurrentTextColor()); 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 /** 719 * Static {@link LockPatternUtils} and {@link DevicePolicyManager} wrapper for testing purposes. 720 * {@link LockPatternUtils} is an internal API not supported by robolectric. 721 * {@link DevicePolicyManager} has a {@code getProfileParent} not yet suppored by robolectric. 722 */ 723 @VisibleForTesting 724 static Proxy sProxy = new Proxy(); 725 726 @VisibleForTesting 727 static class Proxy { isSeparateProfileChallengeEnabled(LockPatternUtils utils, int userHandle)728 public boolean isSeparateProfileChallengeEnabled(LockPatternUtils utils, int userHandle) { 729 return utils.isSeparateProfileChallengeEnabled(userHandle); 730 } 731 getParentProfileInstance(DevicePolicyManager dpm, UserInfo ui)732 public DevicePolicyManager getParentProfileInstance(DevicePolicyManager dpm, UserInfo ui) { 733 return dpm.getParentProfileInstance(ui); 734 } 735 } 736 } 737