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