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