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