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