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