RestrictedLockUtils.java revision 39cef94f1a70358cc1d6e8261379de627c3779be
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 17package com.android.settingslib; 18 19import android.app.AppGlobals; 20import android.app.admin.DevicePolicyManager; 21import android.content.ComponentName; 22import android.content.Context; 23import android.content.Intent; 24import android.content.pm.IPackageManager; 25import android.content.pm.UserInfo; 26import android.graphics.Color; 27import android.graphics.drawable.Drawable; 28import android.os.Bundle; 29import android.os.RemoteException; 30import android.os.UserHandle; 31import android.os.UserManager; 32import android.provider.Settings; 33import android.text.Spanned; 34import android.text.SpannableStringBuilder; 35import android.text.style.ForegroundColorSpan; 36import android.text.style.ImageSpan; 37import android.view.MenuItem; 38import android.widget.TextView; 39 40import com.android.internal.widget.LockPatternUtils; 41 42import java.util.List; 43 44/** 45 * Utility class to host methods usable in adding a restricted padlock icon and showing admin 46 * support message dialog. 47 */ 48public class RestrictedLockUtils { 49 /** 50 * @return drawables for displaying with settings that are locked by a device admin. 51 */ 52 public static Drawable getRestrictedPadlock(Context context) { 53 Drawable restrictedPadlock = context.getDrawable(R.drawable.ic_settings_lock_outline); 54 final int iconSize = context.getResources().getDimensionPixelSize( 55 R.dimen.restricted_lock_icon_size); 56 restrictedPadlock.setBounds(0, 0, iconSize, iconSize); 57 return restrictedPadlock; 58 } 59 60 /** 61 * Checks if a restriction is enforced on a user and returns the enforced admin and 62 * admin userId. 63 * 64 * @param userRestriction Restriction to check 65 * @param userId User which we need to check if restriction is enforced on. 66 * @return EnforcedAdmin Object containing the enforced admin component and admin user details, 67 * or {@code null} If the restriction is not set. If the restriction is set by both device owner 68 * and profile owner, then the admin component will be set to {@code null} and userId to 69 * {@link UserHandle#USER_NULL}. 70 */ 71 public static EnforcedAdmin checkIfRestrictionEnforced(Context context, 72 String userRestriction, int userId) { 73 DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService( 74 Context.DEVICE_POLICY_SERVICE); 75 if (dpm == null) { 76 return null; 77 } 78 ComponentName deviceOwner = dpm.getDeviceOwnerComponentOnAnyUser(); 79 int deviceOwnerUserId = dpm.getDeviceOwnerUserId(); 80 boolean enforcedByDeviceOwner = false; 81 if (deviceOwner != null && deviceOwnerUserId != UserHandle.USER_NULL) { 82 Bundle enforcedRestrictions = dpm.getUserRestrictions(deviceOwner, deviceOwnerUserId); 83 if (enforcedRestrictions != null 84 && enforcedRestrictions.getBoolean(userRestriction, false)) { 85 enforcedByDeviceOwner = true; 86 } 87 } 88 89 ComponentName profileOwner = null; 90 boolean enforcedByProfileOwner = false; 91 if (userId != UserHandle.USER_NULL) { 92 profileOwner = dpm.getProfileOwnerAsUser(userId); 93 if (profileOwner != null) { 94 Bundle enforcedRestrictions = dpm.getUserRestrictions(profileOwner, userId); 95 if (enforcedRestrictions != null 96 && enforcedRestrictions.getBoolean(userRestriction, false)) { 97 enforcedByProfileOwner = true; 98 } 99 } 100 } 101 102 if (!enforcedByDeviceOwner && !enforcedByProfileOwner) { 103 return null; 104 } 105 106 EnforcedAdmin admin = null; 107 if (enforcedByDeviceOwner && enforcedByProfileOwner) { 108 admin = new EnforcedAdmin(); 109 } else if (enforcedByDeviceOwner) { 110 admin = new EnforcedAdmin(deviceOwner, deviceOwnerUserId); 111 } else { 112 admin = new EnforcedAdmin(profileOwner, userId); 113 } 114 return admin; 115 } 116 117 /** 118 * Checks if keyguard features are disabled by policy. 119 * 120 * @param keyguardFeatures Could be any of keyguard features that can be 121 * disabled by {@link android.app.admin.DevicePolicyManager#setKeyguardDisabledFeatures}. 122 * @return EnforcedAdmin Object containing the enforced admin component and admin user details, 123 * or {@code null} If the notification features are not disabled. If the restriction is set by 124 * multiple admins, then the admin component will be set to {@code null} and userId to 125 * {@link UserHandle#USER_NULL}. 126 */ 127 public static EnforcedAdmin checkIfKeyguardFeaturesDisabled(Context context, 128 int keyguardFeatures) { 129 final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService( 130 Context.DEVICE_POLICY_SERVICE); 131 if (dpm == null) { 132 return null; 133 } 134 final UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE); 135 LockPatternUtils lockPatternUtils = new LockPatternUtils(context); 136 EnforcedAdmin enforcedAdmin = null; 137 final int userId = UserHandle.myUserId(); 138 if (um.getUserInfo(userId).isManagedProfile()) { 139 final List<ComponentName> admins = dpm.getActiveAdminsAsUser(userId); 140 if (admins == null) { 141 return null; 142 } 143 for (ComponentName admin : admins) { 144 if ((dpm.getKeyguardDisabledFeatures(admin, userId) & keyguardFeatures) != 0) { 145 if (enforcedAdmin == null) { 146 enforcedAdmin = new EnforcedAdmin(admin, userId); 147 } else { 148 return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN; 149 } 150 } 151 } 152 } else { 153 // Consider all admins for this user and the profiles that are visible from this 154 // user that do not use a separate work challenge. 155 for (UserInfo userInfo : um.getProfiles(userId)) { 156 final List<ComponentName> admins = dpm.getActiveAdminsAsUser(userInfo.id); 157 if (admins == null) { 158 return null; 159 } 160 final boolean isSeparateProfileChallengeEnabled = 161 lockPatternUtils.isSeparateProfileChallengeEnabled(userInfo.id); 162 for (ComponentName admin : admins) { 163 if (!isSeparateProfileChallengeEnabled) { 164 if ((dpm.getKeyguardDisabledFeatures(admin, userInfo.id) 165 & keyguardFeatures) != 0) { 166 if (enforcedAdmin == null) { 167 enforcedAdmin = new EnforcedAdmin(admin, userInfo.id); 168 } else { 169 return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN; 170 } 171 // This same admins could have set policies both on the managed profile 172 // and on the parent. So, if the admin has set the policy on the 173 // managed profile here, we don't need to further check if that admin 174 // has set policy on the parent admin. 175 continue; 176 } 177 } 178 if (userInfo.isManagedProfile()) { 179 // If userInfo.id is a managed profile, we also need to look at 180 // the policies set on the parent. 181 DevicePolicyManager parentDpm = dpm.getParentProfileInstance(admin); 182 if ((parentDpm.getKeyguardDisabledFeatures(admin, userInfo.id) 183 & keyguardFeatures) != 0) { 184 if (enforcedAdmin == null) { 185 enforcedAdmin = new EnforcedAdmin(admin, userInfo.id); 186 } else { 187 return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN; 188 } 189 } 190 } 191 } 192 } 193 } 194 return enforcedAdmin; 195 } 196 197 public static EnforcedAdmin checkIfUninstallBlocked(Context context, 198 String packageName, int userId) { 199 EnforcedAdmin allAppsControlDisallowedAdmin = checkIfRestrictionEnforced(context, 200 UserManager.DISALLOW_APPS_CONTROL, userId); 201 if (allAppsControlDisallowedAdmin != null) { 202 return allAppsControlDisallowedAdmin; 203 } 204 EnforcedAdmin allAppsUninstallDisallowedAdmin = checkIfRestrictionEnforced(context, 205 UserManager.DISALLOW_UNINSTALL_APPS, userId); 206 if (allAppsUninstallDisallowedAdmin != null) { 207 return allAppsUninstallDisallowedAdmin; 208 } 209 IPackageManager ipm = AppGlobals.getPackageManager(); 210 try { 211 if (ipm.getBlockUninstallForUser(packageName, userId)) { 212 DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService( 213 Context.DEVICE_POLICY_SERVICE); 214 if (dpm == null) { 215 return null; 216 } 217 ComponentName admin = dpm.getProfileOwner(); 218 if (admin == null) { 219 admin = dpm.getDeviceOwnerComponentOnCallingUser(); 220 } 221 return new EnforcedAdmin(admin, UserHandle.myUserId()); 222 } 223 } catch (RemoteException e) { 224 // Nothing to do 225 } 226 return null; 227 } 228 229 /** 230 * Check if account management for a specific type of account is disabled by admin. 231 * Only a profile or device owner can disable account management. So, we check if account 232 * management is disabled and return profile or device owner on the calling user. 233 * 234 * @return EnforcedAdmin Object containing the enforced admin component and admin user details, 235 * or {@code null} if the account management is not disabled. 236 */ 237 public static EnforcedAdmin checkIfAccountManagementDisabled(Context context, 238 String accountType) { 239 if (accountType == null) { 240 return null; 241 } 242 DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService( 243 Context.DEVICE_POLICY_SERVICE); 244 if (dpm == null) { 245 return null; 246 } 247 boolean isAccountTypeDisabled = false; 248 String[] disabledTypes = dpm.getAccountTypesWithManagementDisabled(); 249 for (String type : disabledTypes) { 250 if (accountType.equals(type)) { 251 isAccountTypeDisabled = true; 252 break; 253 } 254 } 255 if (!isAccountTypeDisabled) { 256 return null; 257 } 258 return getProfileOrDeviceOwnerOnCallingUser(context); 259 } 260 261 /** 262 * Checks if {@link android.app.admin.DevicePolicyManager#setAutoTimeRequired} is enforced 263 * on the device. 264 * 265 * @return EnforcedAdmin Object containing the device owner component and 266 * userId the device owner is running as, or {@code null} setAutoTimeRequired is not enforced. 267 */ 268 public static EnforcedAdmin checkIfAutoTimeRequired(Context context) { 269 DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService( 270 Context.DEVICE_POLICY_SERVICE); 271 if (dpm == null || !dpm.getAutoTimeRequired()) { 272 return null; 273 } 274 ComponentName adminComponent = dpm.getDeviceOwnerComponentOnCallingUser(); 275 return new EnforcedAdmin(adminComponent, UserHandle.myUserId()); 276 } 277 278 /** 279 * Checks if an admin has enforced minimum password quality requirements on the device. 280 * 281 * @return EnforcedAdmin Object containing the enforced admin component and admin user details, 282 * or {@code null} if no quality requirements are set. If the requirements are set by 283 * multiple device admins, then the admin component will be set to {@code null} and userId to 284 * {@link UserHandle#USER_NULL}. 285 * 286 */ 287 public static EnforcedAdmin checkIfPasswordQualityIsSet(Context context) { 288 final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService( 289 Context.DEVICE_POLICY_SERVICE); 290 if (dpm == null) { 291 return null; 292 } 293 boolean isDisabledByMultipleAdmins = false; 294 ComponentName adminComponent = null; 295 List<ComponentName> admins = dpm.getActiveAdmins(); 296 int quality; 297 if (admins != null) { 298 for (ComponentName admin : admins) { 299 quality = dpm.getPasswordQuality(admin); 300 if (quality >= DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) { 301 if (adminComponent == null) { 302 adminComponent = admin; 303 } else { 304 isDisabledByMultipleAdmins = true; 305 break; 306 } 307 } 308 } 309 } 310 EnforcedAdmin enforcedAdmin = null; 311 if (adminComponent != null) { 312 if (!isDisabledByMultipleAdmins) { 313 enforcedAdmin = new EnforcedAdmin(adminComponent, UserHandle.myUserId()); 314 } else { 315 enforcedAdmin = new EnforcedAdmin(); 316 } 317 } 318 return enforcedAdmin; 319 } 320 321 /** 322 * Checks if any admin has set maximum time to lock. 323 * 324 * @return EnforcedAdmin Object containing the enforced admin component and admin user details, 325 * or {@code null} if no admin has set this restriction. If multiple admins has set this, then 326 * the admin component will be set to {@code null} and userId to {@link UserHandle#USER_NULL} 327 */ 328 public static EnforcedAdmin checkIfMaximumTimeToLockIsSet(Context context) { 329 final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService( 330 Context.DEVICE_POLICY_SERVICE); 331 LockPatternUtils lockPatternUtils = new LockPatternUtils(context); 332 EnforcedAdmin enforcedAdmin = null; 333 final int userId = UserHandle.myUserId(); 334 if (lockPatternUtils.isSeparateProfileChallengeEnabled(userId)) { 335 // If the user has a separate challenge, only consider the admins in that user. 336 final List<ComponentName> admins = dpm.getActiveAdminsAsUser(userId); 337 if (admins == null) { 338 return null; 339 } 340 for (ComponentName admin : admins) { 341 if (dpm.getMaximumTimeToLock(admin, userId) > 0) { 342 if (enforcedAdmin == null) { 343 enforcedAdmin = new EnforcedAdmin(admin, userId); 344 } else { 345 return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN; 346 } 347 } 348 } 349 } else { 350 // Return all admins for this user and the profiles that are visible from this 351 // user that do not use a separate work challenge. 352 final UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE); 353 for (UserInfo userInfo : um.getProfiles(userId)) { 354 final List<ComponentName> admins = dpm.getActiveAdminsAsUser(userInfo.id); 355 if (admins == null) { 356 return null; 357 } 358 final boolean isSeparateProfileChallengeEnabled = 359 lockPatternUtils.isSeparateProfileChallengeEnabled(userInfo.id); 360 for (ComponentName admin : admins) { 361 if (!isSeparateProfileChallengeEnabled) { 362 if (dpm.getMaximumTimeToLock(admin, userInfo.id) > 0) { 363 if (enforcedAdmin == null) { 364 enforcedAdmin = new EnforcedAdmin(admin, userInfo.id); 365 } else { 366 return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN; 367 } 368 // This same admins could have set policies both on the managed profile 369 // and on the parent. So, if the admin has set the policy on the 370 // managed profile here, we don't need to further check if that admin 371 // has set policy on the parent admin. 372 continue; 373 } 374 } 375 if (userInfo.isManagedProfile()) { 376 // If userInfo.id is a managed profile, we also need to look at 377 // the policies set on the parent. 378 DevicePolicyManager parentDpm = dpm.getParentProfileInstance(admin); 379 if (parentDpm.getMaximumTimeToLock(admin, userInfo.id) > 0) { 380 if (enforcedAdmin == null) { 381 enforcedAdmin = new EnforcedAdmin(admin, userInfo.id); 382 } else { 383 return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN; 384 } 385 } 386 } 387 } 388 } 389 } 390 return enforcedAdmin; 391 } 392 393 public static EnforcedAdmin getProfileOrDeviceOwnerOnCallingUser(Context context) { 394 final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService( 395 Context.DEVICE_POLICY_SERVICE); 396 if (dpm == null) { 397 return null; 398 } 399 ComponentName adminComponent = dpm.getDeviceOwnerComponentOnCallingUser(); 400 if (adminComponent != null) { 401 return new EnforcedAdmin(adminComponent, UserHandle.myUserId()); 402 } 403 adminComponent = dpm.getProfileOwner(); 404 if (adminComponent != null) { 405 return new EnforcedAdmin(adminComponent, UserHandle.myUserId()); 406 } 407 return null; 408 } 409 410 /** 411 * Set the menu item as disabled by admin by adding a restricted padlock at the end of the 412 * text and set the click listener which will send an intent to show the admin support details 413 * dialog. If the admin is null, remove the padlock and disabled color span. When the admin is 414 * null, we also set the OnMenuItemClickListener to null, so if you want to set a custom 415 * OnMenuItemClickListener, set it after calling this method. 416 */ 417 public static void setMenuItemAsDisabledByAdmin(final Context context, 418 final MenuItem item, final EnforcedAdmin admin) { 419 SpannableStringBuilder sb = new SpannableStringBuilder(item.getTitle()); 420 removeExistingRestrictedSpans(sb); 421 422 if (admin != null) { 423 final int disabledColor = context.getColor(R.color.disabled_text_color); 424 sb.setSpan(new ForegroundColorSpan(disabledColor), 0, sb.length(), 425 Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 426 ImageSpan image = new RestrictedLockImageSpan(context); 427 sb.append(" ", image, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 428 429 item.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { 430 @Override 431 public boolean onMenuItemClick(MenuItem item) { 432 sendShowAdminSupportDetailsIntent(context, admin); 433 return true; 434 } 435 }); 436 } else { 437 item.setOnMenuItemClickListener(null); 438 } 439 item.setTitle(sb); 440 } 441 442 private static void removeExistingRestrictedSpans(SpannableStringBuilder sb) { 443 final int length = sb.length(); 444 RestrictedLockImageSpan[] imageSpans = sb.getSpans(length - 1, length, 445 RestrictedLockImageSpan.class); 446 for (ImageSpan span : imageSpans) { 447 final int start = sb.getSpanStart(span); 448 final int end = sb.getSpanEnd(span); 449 sb.removeSpan(span); 450 sb.delete(start, end); 451 } 452 ForegroundColorSpan[] colorSpans = sb.getSpans(0, length, ForegroundColorSpan.class); 453 for (ForegroundColorSpan span : colorSpans) { 454 sb.removeSpan(span); 455 } 456 } 457 458 /** 459 * Send the intent to trigger the {@link android.settings.ShowAdminSupportDetailsDialog}. 460 */ 461 public static void sendShowAdminSupportDetailsIntent(Context context, EnforcedAdmin admin) { 462 final Intent intent = getShowAdminSupportDetailsIntent(context, admin); 463 int adminUserId = UserHandle.myUserId(); 464 if (admin.userId != UserHandle.USER_NULL) { 465 adminUserId = admin.userId; 466 } 467 context.startActivityAsUser(intent, new UserHandle(adminUserId)); 468 } 469 470 public static Intent getShowAdminSupportDetailsIntent(Context context, EnforcedAdmin admin) { 471 final Intent intent = new Intent(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS); 472 if (admin != null) { 473 if (admin.component != null) { 474 intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, admin.component); 475 } 476 int adminUserId = UserHandle.myUserId(); 477 if (admin.userId != UserHandle.USER_NULL) { 478 adminUserId = admin.userId; 479 } 480 intent.putExtra(Intent.EXTRA_USER_ID, adminUserId); 481 } 482 return intent; 483 } 484 485 public static void setTextViewPadlock(Context context, 486 TextView textView, boolean showPadlock) { 487 final SpannableStringBuilder sb = new SpannableStringBuilder(textView.getText()); 488 removeExistingRestrictedSpans(sb); 489 if (showPadlock) { 490 final ImageSpan image = new RestrictedLockImageSpan(context); 491 sb.append(" ", image, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 492 } 493 textView.setText(sb); 494 } 495 496 /** 497 * Takes a {@link android.widget.TextView} and applies an alpha so that the text looks like 498 * disabled and appends a padlock to the text. This assumes that there are no 499 * ForegroundColorSpans and RestrictedLockImageSpans used on the TextView. 500 */ 501 public static void setTextViewAsDisabledByAdmin(Context context, 502 TextView textView, boolean disabled) { 503 final SpannableStringBuilder sb = new SpannableStringBuilder(textView.getText()); 504 removeExistingRestrictedSpans(sb); 505 if (disabled) { 506 final int disabledColor = context.getColor(R.color.disabled_text_color); 507 sb.setSpan(new ForegroundColorSpan(disabledColor), 0, sb.length(), 508 Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 509 final ImageSpan image = new RestrictedLockImageSpan(context); 510 sb.append(" ", image, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 511 } 512 textView.setText(sb); 513 } 514 515 public static class EnforcedAdmin { 516 public ComponentName component = null; 517 public int userId = UserHandle.USER_NULL; 518 519 // We use this to represent the case where a policy is enforced by multiple admins. 520 public final static EnforcedAdmin MULTIPLE_ENFORCED_ADMIN = new EnforcedAdmin(); 521 522 public EnforcedAdmin(ComponentName component, int userId) { 523 this.component = component; 524 this.userId = userId; 525 } 526 527 public EnforcedAdmin(EnforcedAdmin other) { 528 if (other == null) { 529 throw new IllegalArgumentException(); 530 } 531 this.component = other.component; 532 this.userId = other.userId; 533 } 534 535 public EnforcedAdmin() {} 536 537 @Override 538 public boolean equals(Object object) { 539 if (object == this) return true; 540 if (!(object instanceof EnforcedAdmin)) return false; 541 EnforcedAdmin other = (EnforcedAdmin) object; 542 if (userId != other.userId) { 543 return false; 544 } 545 if ((component == null && other == null) || 546 (component != null && component.equals(other))) { 547 return true; 548 } 549 return false; 550 } 551 552 @Override 553 public String toString() { 554 return "EnforcedAdmin{component=" + component + ",userId=" + userId + "}"; 555 } 556 557 public void copyTo(EnforcedAdmin other) { 558 if (other == null) { 559 throw new IllegalArgumentException(); 560 } 561 other.component = component; 562 other.userId = userId; 563 } 564 } 565} 566