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