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