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
17package com.android.settingslib;
18
19import android.app.AppGlobals;
20import android.app.admin.DevicePolicyManager;
21import android.content.ComponentName;
22import android.content.Context;
23import android.content.Intent;
24import android.content.pm.IPackageManager;
25import android.content.pm.UserInfo;
26import android.graphics.drawable.Drawable;
27import android.os.RemoteException;
28import android.os.UserHandle;
29import android.os.UserManager;
30import android.provider.Settings;
31import android.text.Spanned;
32import android.text.SpannableStringBuilder;
33import android.text.style.ForegroundColorSpan;
34import android.text.style.ImageSpan;
35import android.view.MenuItem;
36import android.widget.TextView;
37
38import com.android.internal.widget.LockPatternUtils;
39
40import 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 */
46public class RestrictedLockUtils {
47    /**
48     * @return drawables for displaying with settings that are locked by a device admin.
49     */
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     */
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
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     */
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
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     */
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
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     */
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
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
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     */
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     */
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     */
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     */
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
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
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
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     */
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
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     */
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
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
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
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
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     */
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
704        public EnforcedAdmin(ComponentName component, int userId) {
705            this.component = component;
706            this.userId = userId;
707        }
708
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
717        public EnforcedAdmin() {}
718
719        @Override
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
735        public String toString() {
736            return "EnforcedAdmin{component=" + component + ",userId=" + userId + "}";
737        }
738
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