package com.android.settingslib;
import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NONE;
import static android.app.admin.DevicePolicyManager.PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.AppGlobals;
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.graphics.drawable.Drawable;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import android.support.annotation.VisibleForTesting;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.style.ForegroundColorSpan;
import android.text.style.ImageSpan;
import android.view.MenuItem;
import android.widget.TextView;
import com.android.internal.widget.LockPatternUtils;
import java.util.List;
import java.util.Objects;
public class RestrictedLockUtils {
public static Drawable getRestrictedPadlock(Context context) {
Drawable restrictedPadlock = context.getDrawable(R.drawable.ic_info);
final int iconSize = context.getResources().getDimensionPixelSize(
R.dimen.restricted_icon_size);
restrictedPadlock.setBounds(0, 0, iconSize, iconSize);
return restrictedPadlock;
}
public static EnforcedAdmin checkIfRestrictionEnforced(Context context,
String userRestriction, int userId) {
final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
Context.DEVICE_POLICY_SERVICE);
if (dpm == null) {
return null;
}
final UserManager um = UserManager.get(context);
final List<UserManager.EnforcingUser> enforcingUsers =
um.getUserRestrictionSources(userRestriction, UserHandle.of(userId));
if (enforcingUsers.isEmpty()) {
return null;
} else if (enforcingUsers.size() > 1) {
return EnforcedAdmin.createDefaultEnforcedAdminWithRestriction(userRestriction);
}
final int restrictionSource = enforcingUsers.get(0).getUserRestrictionSource();
final int adminUserId = enforcingUsers.get(0).getUserHandle().getIdentifier();
if (restrictionSource == UserManager.RESTRICTION_SOURCE_PROFILE_OWNER) {
if (adminUserId == userId) {
return getProfileOwner(context, userRestriction, adminUserId);
} else {
final UserInfo parentUser = um.getProfileParent(adminUserId);
return (parentUser != null && parentUser.id == userId)
? getProfileOwner(context, userRestriction, adminUserId)
: EnforcedAdmin.createDefaultEnforcedAdminWithRestriction(userRestriction);
}
} else if (restrictionSource == UserManager.RESTRICTION_SOURCE_DEVICE_OWNER) {
return adminUserId == userId
? getDeviceOwner(context, userRestriction)
: EnforcedAdmin.createDefaultEnforcedAdminWithRestriction(userRestriction);
}
return null;
}
public static boolean hasBaseUserRestriction(Context context,
String userRestriction, int userId) {
final UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
return um.hasBaseUserRestriction(userRestriction, UserHandle.of(userId));
}
public static EnforcedAdmin checkIfKeyguardFeaturesDisabled(Context context,
int keyguardFeatures, final @UserIdInt int userId) {
final LockSettingCheck check = (dpm, admin, checkUser) -> {
int effectiveFeatures = dpm.getKeyguardDisabledFeatures(admin, checkUser);
if (checkUser != userId) {
effectiveFeatures &= PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER;
}
return (effectiveFeatures & keyguardFeatures) != KEYGUARD_DISABLE_FEATURES_NONE;
};
if (UserManager.get(context).getUserInfo(userId).isManagedProfile()) {
DevicePolicyManager dpm =
(DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
return findEnforcedAdmin(dpm.getActiveAdminsAsUser(userId), dpm, userId, check);
}
return checkForLockSetting(context, userId, check);
}
@Nullable
private static EnforcedAdmin findEnforcedAdmin(@Nullable List<ComponentName> admins,
@NonNull DevicePolicyManager dpm, @UserIdInt int userId,
@NonNull LockSettingCheck check) {
if (admins == null) {
return null;
}
EnforcedAdmin enforcedAdmin = null;
for (ComponentName admin : admins) {
if (check.isEnforcing(dpm, admin, userId)) {
if (enforcedAdmin == null) {
enforcedAdmin = new EnforcedAdmin(admin, userId);
} else {
return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN;
}
}
}
return enforcedAdmin;
}
public static EnforcedAdmin checkIfUninstallBlocked(Context context,
String packageName, int userId) {
EnforcedAdmin allAppsControlDisallowedAdmin = checkIfRestrictionEnforced(context,
UserManager.DISALLOW_APPS_CONTROL, userId);
if (allAppsControlDisallowedAdmin != null) {
return allAppsControlDisallowedAdmin;
}
EnforcedAdmin allAppsUninstallDisallowedAdmin = checkIfRestrictionEnforced(context,
UserManager.DISALLOW_UNINSTALL_APPS, userId);
if (allAppsUninstallDisallowedAdmin != null) {
return allAppsUninstallDisallowedAdmin;
}
IPackageManager ipm = AppGlobals.getPackageManager();
try {
if (ipm.getBlockUninstallForUser(packageName, userId)) {
return getProfileOrDeviceOwner(context, userId);
}
} catch (RemoteException e) {
}
return null;
}
public static EnforcedAdmin checkIfApplicationIsSuspended(Context context, String packageName,
int userId) {
IPackageManager ipm = AppGlobals.getPackageManager();
try {
if (ipm.isPackageSuspendedForUser(packageName, userId)) {
return getProfileOrDeviceOwner(context, userId);
}
} catch (RemoteException | IllegalArgumentException e) {
}
return null;
}
public static EnforcedAdmin checkIfInputMethodDisallowed(Context context,
String packageName, int userId) {
DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
Context.DEVICE_POLICY_SERVICE);
if (dpm == null) {
return null;
}
EnforcedAdmin admin = getProfileOrDeviceOwner(context, userId);
boolean permitted = true;
if (admin != null) {
permitted = dpm.isInputMethodPermittedByAdmin(admin.component,
packageName, userId);
}
int managedProfileId = getManagedProfileId(context, userId);
EnforcedAdmin profileAdmin = getProfileOrDeviceOwner(context, managedProfileId);
boolean permittedByProfileAdmin = true;
if (profileAdmin != null) {
permittedByProfileAdmin = dpm.isInputMethodPermittedByAdmin(profileAdmin.component,
packageName, managedProfileId);
}
if (!permitted && !permittedByProfileAdmin) {
return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN;
} else if (!permitted) {
return admin;
} else if (!permittedByProfileAdmin) {
return profileAdmin;
}
return null;
}
public static EnforcedAdmin checkIfRemoteContactSearchDisallowed(Context context, int userId) {
DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
Context.DEVICE_POLICY_SERVICE);
if (dpm == null) {
return null;
}
EnforcedAdmin admin = getProfileOwner(context, userId);
if (admin == null) {
return null;
}
UserHandle userHandle = UserHandle.of(userId);
if (dpm.getCrossProfileContactsSearchDisabled(userHandle)
&& dpm.getCrossProfileCallerIdDisabled(userHandle)) {
return admin;
}
return null;
}
public static EnforcedAdmin checkIfAccessibilityServiceDisallowed(Context context,
String packageName, int userId) {
DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
Context.DEVICE_POLICY_SERVICE);
if (dpm == null) {
return null;
}
EnforcedAdmin admin = getProfileOrDeviceOwner(context, userId);
boolean permitted = true;
if (admin != null) {
permitted = dpm.isAccessibilityServicePermittedByAdmin(admin.component,
packageName, userId);
}
int managedProfileId = getManagedProfileId(context, userId);
EnforcedAdmin profileAdmin = getProfileOrDeviceOwner(context, managedProfileId);
boolean permittedByProfileAdmin = true;
if (profileAdmin != null) {
permittedByProfileAdmin = dpm.isAccessibilityServicePermittedByAdmin(
profileAdmin.component, packageName, managedProfileId);
}
if (!permitted && !permittedByProfileAdmin) {
return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN;
} else if (!permitted) {
return admin;
} else if (!permittedByProfileAdmin) {
return profileAdmin;
}
return null;
}
private static int getManagedProfileId(Context context, int userId) {
UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
List<UserInfo> userProfiles = um.getProfiles(userId);
for (UserInfo uInfo : userProfiles) {
if (uInfo.id == userId) {
continue;
}
if (uInfo.isManagedProfile()) {
return uInfo.id;
}
}
return UserHandle.USER_NULL;
}
public static EnforcedAdmin checkIfAccountManagementDisabled(Context context,
String accountType, int userId) {
if (accountType == null) {
return null;
}
DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
Context.DEVICE_POLICY_SERVICE);
PackageManager pm = context.getPackageManager();
if (!pm.hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN) || dpm == null) {
return null;
}
boolean isAccountTypeDisabled = false;
String[] disabledTypes = dpm.getAccountTypesWithManagementDisabledAsUser(userId);
for (String type : disabledTypes) {
if (accountType.equals(type)) {
isAccountTypeDisabled = true;
break;
}
}
if (!isAccountTypeDisabled) {
return null;
}
return getProfileOrDeviceOwner(context, userId);
}
public static EnforcedAdmin checkIfMeteredDataRestricted(Context context,
String packageName, int userId) {
final EnforcedAdmin enforcedAdmin = getProfileOrDeviceOwner(context, userId);
if (enforcedAdmin == null) {
return null;
}
final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
Context.DEVICE_POLICY_SERVICE);
return dpm.isMeteredDataDisabledPackageForUser(enforcedAdmin.component, packageName, userId)
? enforcedAdmin : null;
}
public static EnforcedAdmin checkIfAutoTimeRequired(Context context) {
DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
Context.DEVICE_POLICY_SERVICE);
if (dpm == null || !dpm.getAutoTimeRequired()) {
return null;
}
ComponentName adminComponent = dpm.getDeviceOwnerComponentOnCallingUser();
return new EnforcedAdmin(adminComponent, UserHandle.myUserId());
}
public static EnforcedAdmin checkIfPasswordQualityIsSet(Context context, int userId) {
final LockSettingCheck check =
(DevicePolicyManager dpm, ComponentName admin, @UserIdInt int checkUser) ->
dpm.getPasswordQuality(admin, checkUser)
> DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
Context.DEVICE_POLICY_SERVICE);
if (dpm == null) {
return null;
}
LockPatternUtils lockPatternUtils = new LockPatternUtils(context);
if (sProxy.isSeparateProfileChallengeEnabled(lockPatternUtils, userId)) {
final List<ComponentName> admins = dpm.getActiveAdminsAsUser(userId);
if (admins == null) {
return null;
}
EnforcedAdmin enforcedAdmin = null;
for (ComponentName admin : admins) {
if (check.isEnforcing(dpm, admin, userId)) {
if (enforcedAdmin == null) {
enforcedAdmin = new EnforcedAdmin(admin, userId);
} else {
return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN;
}
}
}
return enforcedAdmin;
} else {
return checkForLockSetting(context, userId, check);
}
}
public static EnforcedAdmin checkIfMaximumTimeToLockIsSet(Context context) {
return checkForLockSetting(context, UserHandle.myUserId(),
(DevicePolicyManager dpm, ComponentName admin, @UserIdInt int userId) ->
dpm.getMaximumTimeToLock(admin, userId) > 0);
}
private interface LockSettingCheck {
boolean isEnforcing(DevicePolicyManager dpm, ComponentName admin, @UserIdInt int userId);
}
private static EnforcedAdmin checkForLockSetting(
Context context, @UserIdInt int userId, LockSettingCheck check) {
final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
Context.DEVICE_POLICY_SERVICE);
if (dpm == null) {
return null;
}
final LockPatternUtils lockPatternUtils = new LockPatternUtils(context);
EnforcedAdmin enforcedAdmin = null;
for (UserInfo userInfo : UserManager.get(context).getProfiles(userId)) {
final List<ComponentName> admins = dpm.getActiveAdminsAsUser(userInfo.id);
if (admins == null) {
continue;
}
final boolean isSeparateProfileChallengeEnabled =
sProxy.isSeparateProfileChallengeEnabled(lockPatternUtils, userInfo.id);
for (ComponentName admin : admins) {
if (!isSeparateProfileChallengeEnabled) {
if (check.isEnforcing(dpm, admin, userInfo.id)) {
if (enforcedAdmin == null) {
enforcedAdmin = new EnforcedAdmin(admin, userInfo.id);
} else {
return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN;
}
continue;
}
}
if (userInfo.isManagedProfile()) {
DevicePolicyManager parentDpm = sProxy.getParentProfileInstance(dpm, userInfo);
if (check.isEnforcing(parentDpm, admin, userInfo.id)) {
if (enforcedAdmin == null) {
enforcedAdmin = new EnforcedAdmin(admin, userInfo.id);
} else {
return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN;
}
}
}
}
}
return enforcedAdmin;
}
public static EnforcedAdmin getProfileOrDeviceOwner(Context context, int userId) {
return getProfileOrDeviceOwner(context, null, userId);
}
public static EnforcedAdmin getProfileOrDeviceOwner(
Context context, String enforcedRestriction, int userId) {
if (userId == UserHandle.USER_NULL) {
return null;
}
final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
Context.DEVICE_POLICY_SERVICE);
if (dpm == null) {
return null;
}
ComponentName adminComponent = dpm.getProfileOwnerAsUser(userId);
if (adminComponent != null) {
return new EnforcedAdmin(adminComponent, enforcedRestriction, userId);
}
if (dpm.getDeviceOwnerUserId() == userId) {
adminComponent = dpm.getDeviceOwnerComponentOnAnyUser();
if (adminComponent != null) {
return new EnforcedAdmin(adminComponent, enforcedRestriction, userId);
}
}
return null;
}
public static EnforcedAdmin getDeviceOwner(Context context) {
return getDeviceOwner(context, null);
}
private static EnforcedAdmin getDeviceOwner(Context context, String enforcedRestriction) {
final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
Context.DEVICE_POLICY_SERVICE);
if (dpm == null) {
return null;
}
ComponentName adminComponent = dpm.getDeviceOwnerComponentOnAnyUser();
if (adminComponent != null) {
return new EnforcedAdmin(
adminComponent, enforcedRestriction, dpm.getDeviceOwnerUserId());
}
return null;
}
private static EnforcedAdmin getProfileOwner(Context context, int userId) {
return getProfileOwner(context, null, userId);
}
private static EnforcedAdmin getProfileOwner(
Context context, String enforcedRestriction, int userId) {
if (userId == UserHandle.USER_NULL) {
return null;
}
final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
Context.DEVICE_POLICY_SERVICE);
if (dpm == null) {
return null;
}
ComponentName adminComponent = dpm.getProfileOwnerAsUser(userId);
if (adminComponent != null) {
return new EnforcedAdmin(adminComponent, enforcedRestriction, userId);
}
return null;
}
public static void setMenuItemAsDisabledByAdmin(final Context context,
final MenuItem item, final EnforcedAdmin admin) {
SpannableStringBuilder sb = new SpannableStringBuilder(item.getTitle());
removeExistingRestrictedSpans(sb);
if (admin != null) {
final int disabledColor = context.getColor(R.color.disabled_text_color);
sb.setSpan(new ForegroundColorSpan(disabledColor), 0, sb.length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
ImageSpan image = new RestrictedLockImageSpan(context);
sb.append(" ", image, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
item.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
sendShowAdminSupportDetailsIntent(context, admin);
return true;
}
});
} else {
item.setOnMenuItemClickListener(null);
}
item.setTitle(sb);
}
private static void removeExistingRestrictedSpans(SpannableStringBuilder sb) {
final int length = sb.length();
RestrictedLockImageSpan[] imageSpans = sb.getSpans(length - 1, length,
RestrictedLockImageSpan.class);
for (ImageSpan span : imageSpans) {
final int start = sb.getSpanStart(span);
final int end = sb.getSpanEnd(span);
sb.removeSpan(span);
sb.delete(start, end);
}
ForegroundColorSpan[] colorSpans = sb.getSpans(0, length, ForegroundColorSpan.class);
for (ForegroundColorSpan span : colorSpans) {
sb.removeSpan(span);
}
}
public static void sendShowAdminSupportDetailsIntent(Context context, EnforcedAdmin admin) {
final Intent intent = getShowAdminSupportDetailsIntent(context, admin);
int targetUserId = UserHandle.myUserId();
if (admin != null && admin.userId != UserHandle.USER_NULL
&& isCurrentUserOrProfile(context, admin.userId)) {
targetUserId = admin.userId;
}
intent.putExtra(DevicePolicyManager.EXTRA_RESTRICTION, admin.enforcedRestriction);
context.startActivityAsUser(intent, new UserHandle(targetUserId));
}
public static Intent getShowAdminSupportDetailsIntent(Context context, EnforcedAdmin admin) {
final Intent intent = new Intent(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS);
if (admin != null) {
if (admin.component != null) {
intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, admin.component);
}
int adminUserId = UserHandle.myUserId();
if (admin.userId != UserHandle.USER_NULL) {
adminUserId = admin.userId;
}
intent.putExtra(Intent.EXTRA_USER_ID, adminUserId);
}
return intent;
}
public static boolean isCurrentUserOrProfile(Context context, int userId) {
UserManager um = UserManager.get(context);
for (UserInfo userInfo : um.getProfiles(UserHandle.myUserId())) {
if (userInfo.id == userId) {
return true;
}
}
return false;
}
public static boolean isAdminInCurrentUserOrProfile(Context context, ComponentName admin) {
DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
Context.DEVICE_POLICY_SERVICE);
UserManager um = UserManager.get(context);
for (UserInfo userInfo : um.getProfiles(UserHandle.myUserId())) {
if (dpm.isAdminActiveAsUser(admin, userInfo.id)) {
return true;
}
}
return false;
}
public static void setTextViewPadlock(Context context,
TextView textView, boolean showPadlock) {
final SpannableStringBuilder sb = new SpannableStringBuilder(textView.getText());
removeExistingRestrictedSpans(sb);
if (showPadlock) {
final ImageSpan image = new RestrictedLockImageSpan(context);
sb.append(" ", image, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
textView.setText(sb);
}
public static void setTextViewAsDisabledByAdmin(Context context,
TextView textView, boolean disabled) {
final SpannableStringBuilder sb = new SpannableStringBuilder(textView.getText());
removeExistingRestrictedSpans(sb);
if (disabled) {
final int disabledColor = context.getColor(R.color.disabled_text_color);
sb.setSpan(new ForegroundColorSpan(disabledColor), 0, sb.length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
textView.setCompoundDrawables(null, null, getRestrictedPadlock(context), null);
textView.setCompoundDrawablePadding(context.getResources().getDimensionPixelSize(
R.dimen.restricted_icon_padding));
} else {
textView.setCompoundDrawables(null, null, null, null);
}
textView.setText(sb);
}
public static class EnforcedAdmin {
@Nullable
public ComponentName component = null;
@Nullable
public String enforcedRestriction = null;
public int userId = UserHandle.USER_NULL;
public final static EnforcedAdmin MULTIPLE_ENFORCED_ADMIN = new EnforcedAdmin();
public static EnforcedAdmin createDefaultEnforcedAdminWithRestriction(
String enforcedRestriction) {
EnforcedAdmin enforcedAdmin = new EnforcedAdmin();
enforcedAdmin.enforcedRestriction = enforcedRestriction;
return enforcedAdmin;
}
public EnforcedAdmin(ComponentName component, int userId) {
this.component = component;
this.userId = userId;
}
public EnforcedAdmin(ComponentName component, String enforcedRestriction, int userId) {
this.component = component;
this.enforcedRestriction = enforcedRestriction;
this.userId = userId;
}
public EnforcedAdmin(EnforcedAdmin other) {
if (other == null) {
throw new IllegalArgumentException();
}
this.component = other.component;
this.enforcedRestriction = other.enforcedRestriction;
this.userId = other.userId;
}
public EnforcedAdmin() {
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
EnforcedAdmin that = (EnforcedAdmin) o;
return userId == that.userId &&
Objects.equals(component, that.component) &&
Objects.equals(enforcedRestriction, that.enforcedRestriction);
}
@Override
public int hashCode() {
return Objects.hash(component, enforcedRestriction, userId);
}
@Override
public String toString() {
return "EnforcedAdmin{" +
"component=" + component +
", enforcedRestriction='" + enforcedRestriction +
", userId=" + userId +
'}';
}
}
@VisibleForTesting
static Proxy sProxy = new Proxy();
@VisibleForTesting
static class Proxy {
public boolean isSeparateProfileChallengeEnabled(LockPatternUtils utils, int userHandle) {
return utils.isSeparateProfileChallengeEnabled(userHandle);
}
public DevicePolicyManager getParentProfileInstance(DevicePolicyManager dpm, UserInfo ui) {
return dpm.getParentProfileInstance(ui);
}
}
}