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