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