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