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