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