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