RestrictedLockUtils.java revision 39cef94f1a70358cc1d6e8261379de627c3779be
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.Color;
27import android.graphics.drawable.Drawable;
28import android.os.Bundle;
29import android.os.RemoteException;
30import android.os.UserHandle;
31import android.os.UserManager;
32import android.provider.Settings;
33import android.text.Spanned;
34import android.text.SpannableStringBuilder;
35import android.text.style.ForegroundColorSpan;
36import android.text.style.ImageSpan;
37import android.view.MenuItem;
38import android.widget.TextView;
39
40import com.android.internal.widget.LockPatternUtils;
41
42import java.util.List;
43
44/**
45 * Utility class to host methods usable in adding a restricted padlock icon and showing admin
46 * support message dialog.
47 */
48public class RestrictedLockUtils {
49    /**
50     * @return drawables for displaying with settings that are locked by a device admin.
51     */
52    public static Drawable getRestrictedPadlock(Context context) {
53        Drawable restrictedPadlock = context.getDrawable(R.drawable.ic_settings_lock_outline);
54        final int iconSize = context.getResources().getDimensionPixelSize(
55                R.dimen.restricted_lock_icon_size);
56        restrictedPadlock.setBounds(0, 0, iconSize, iconSize);
57        return restrictedPadlock;
58    }
59
60    /**
61     * Checks if a restriction is enforced on a user and returns the enforced admin and
62     * admin userId.
63     *
64     * @param userRestriction Restriction to check
65     * @param userId User which we need to check if restriction is enforced on.
66     * @return EnforcedAdmin Object containing the enforced admin component and admin user details,
67     * or {@code null} If the restriction is not set. If the restriction is set by both device owner
68     * and profile owner, then the admin component will be set to {@code null} and userId to
69     * {@link UserHandle#USER_NULL}.
70     */
71    public static EnforcedAdmin checkIfRestrictionEnforced(Context context,
72            String userRestriction, int userId) {
73        DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
74                Context.DEVICE_POLICY_SERVICE);
75        if (dpm == null) {
76            return null;
77        }
78        ComponentName deviceOwner = dpm.getDeviceOwnerComponentOnAnyUser();
79        int deviceOwnerUserId = dpm.getDeviceOwnerUserId();
80        boolean enforcedByDeviceOwner = false;
81        if (deviceOwner != null && deviceOwnerUserId != UserHandle.USER_NULL) {
82            Bundle enforcedRestrictions = dpm.getUserRestrictions(deviceOwner, deviceOwnerUserId);
83            if (enforcedRestrictions != null
84                    && enforcedRestrictions.getBoolean(userRestriction, false)) {
85                enforcedByDeviceOwner = true;
86            }
87        }
88
89        ComponentName profileOwner = null;
90        boolean enforcedByProfileOwner = false;
91        if (userId != UserHandle.USER_NULL) {
92            profileOwner = dpm.getProfileOwnerAsUser(userId);
93            if (profileOwner != null) {
94                Bundle enforcedRestrictions = dpm.getUserRestrictions(profileOwner, userId);
95                if (enforcedRestrictions != null
96                        && enforcedRestrictions.getBoolean(userRestriction, false)) {
97                    enforcedByProfileOwner = true;
98                }
99            }
100        }
101
102        if (!enforcedByDeviceOwner && !enforcedByProfileOwner) {
103            return null;
104        }
105
106        EnforcedAdmin admin = null;
107        if (enforcedByDeviceOwner && enforcedByProfileOwner) {
108            admin = new EnforcedAdmin();
109        } else if (enforcedByDeviceOwner) {
110            admin = new EnforcedAdmin(deviceOwner, deviceOwnerUserId);
111        } else {
112            admin = new EnforcedAdmin(profileOwner, userId);
113        }
114        return admin;
115    }
116
117    /**
118     * Checks if keyguard features are disabled by policy.
119     *
120     * @param keyguardFeatures Could be any of keyguard features that can be
121     * disabled by {@link android.app.admin.DevicePolicyManager#setKeyguardDisabledFeatures}.
122     * @return EnforcedAdmin Object containing the enforced admin component and admin user details,
123     * or {@code null} If the notification features are not disabled. If the restriction is set by
124     * multiple admins, then the admin component will be set to {@code null} and userId to
125     * {@link UserHandle#USER_NULL}.
126     */
127    public static EnforcedAdmin checkIfKeyguardFeaturesDisabled(Context context,
128            int keyguardFeatures) {
129        final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
130                Context.DEVICE_POLICY_SERVICE);
131        if (dpm == null) {
132            return null;
133        }
134        final UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
135        LockPatternUtils lockPatternUtils = new LockPatternUtils(context);
136        EnforcedAdmin enforcedAdmin = null;
137        final int userId = UserHandle.myUserId();
138        if (um.getUserInfo(userId).isManagedProfile()) {
139            final List<ComponentName> admins = dpm.getActiveAdminsAsUser(userId);
140            if (admins == null) {
141                return null;
142            }
143            for (ComponentName admin : admins) {
144                if ((dpm.getKeyguardDisabledFeatures(admin, userId) & keyguardFeatures) != 0) {
145                    if (enforcedAdmin == null) {
146                        enforcedAdmin = new EnforcedAdmin(admin, userId);
147                    } else {
148                        return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN;
149                    }
150                }
151            }
152        } else {
153            // Consider all admins for this user and the profiles that are visible from this
154            // user that do not use a separate work challenge.
155            for (UserInfo userInfo : um.getProfiles(userId)) {
156                final List<ComponentName> admins = dpm.getActiveAdminsAsUser(userInfo.id);
157                if (admins == null) {
158                    return null;
159                }
160                final boolean isSeparateProfileChallengeEnabled =
161                        lockPatternUtils.isSeparateProfileChallengeEnabled(userInfo.id);
162                for (ComponentName admin : admins) {
163                    if (!isSeparateProfileChallengeEnabled) {
164                        if ((dpm.getKeyguardDisabledFeatures(admin, userInfo.id)
165                                    & keyguardFeatures) != 0) {
166                            if (enforcedAdmin == null) {
167                                enforcedAdmin = new EnforcedAdmin(admin, userInfo.id);
168                            } else {
169                                return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN;
170                            }
171                            // This same admins could have set policies both on the managed profile
172                            // and on the parent. So, if the admin has set the policy on the
173                            // managed profile here, we don't need to further check if that admin
174                            // has set policy on the parent admin.
175                            continue;
176                        }
177                    }
178                    if (userInfo.isManagedProfile()) {
179                        // If userInfo.id is a managed profile, we also need to look at
180                        // the policies set on the parent.
181                        DevicePolicyManager parentDpm = dpm.getParentProfileInstance(admin);
182                        if ((parentDpm.getKeyguardDisabledFeatures(admin, userInfo.id)
183                                & keyguardFeatures) != 0) {
184                            if (enforcedAdmin == null) {
185                                enforcedAdmin = new EnforcedAdmin(admin, userInfo.id);
186                            } else {
187                                return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN;
188                            }
189                        }
190                    }
191                }
192            }
193        }
194        return enforcedAdmin;
195    }
196
197    public static EnforcedAdmin checkIfUninstallBlocked(Context context,
198            String packageName, int userId) {
199        EnforcedAdmin allAppsControlDisallowedAdmin = checkIfRestrictionEnforced(context,
200                UserManager.DISALLOW_APPS_CONTROL, userId);
201        if (allAppsControlDisallowedAdmin != null) {
202            return allAppsControlDisallowedAdmin;
203        }
204        EnforcedAdmin allAppsUninstallDisallowedAdmin = checkIfRestrictionEnforced(context,
205                UserManager.DISALLOW_UNINSTALL_APPS, userId);
206        if (allAppsUninstallDisallowedAdmin != null) {
207            return allAppsUninstallDisallowedAdmin;
208        }
209        IPackageManager ipm = AppGlobals.getPackageManager();
210        try {
211            if (ipm.getBlockUninstallForUser(packageName, userId)) {
212                DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
213                        Context.DEVICE_POLICY_SERVICE);
214                if (dpm == null) {
215                    return null;
216                }
217                ComponentName admin = dpm.getProfileOwner();
218                if (admin == null) {
219                    admin = dpm.getDeviceOwnerComponentOnCallingUser();
220                }
221                return new EnforcedAdmin(admin, UserHandle.myUserId());
222            }
223        } catch (RemoteException e) {
224            // Nothing to do
225        }
226        return null;
227    }
228
229    /**
230     * Check if account management for a specific type of account is disabled by admin.
231     * Only a profile or device owner can disable account management. So, we check if account
232     * management is disabled and return profile or device owner on the calling user.
233     *
234     * @return EnforcedAdmin Object containing the enforced admin component and admin user details,
235     * or {@code null} if the account management is not disabled.
236     */
237    public static EnforcedAdmin checkIfAccountManagementDisabled(Context context,
238            String accountType) {
239        if (accountType == null) {
240            return null;
241        }
242        DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
243                Context.DEVICE_POLICY_SERVICE);
244        if (dpm == null) {
245            return null;
246        }
247        boolean isAccountTypeDisabled = false;
248        String[] disabledTypes = dpm.getAccountTypesWithManagementDisabled();
249        for (String type : disabledTypes) {
250            if (accountType.equals(type)) {
251                isAccountTypeDisabled = true;
252                break;
253            }
254        }
255        if (!isAccountTypeDisabled) {
256            return null;
257        }
258        return getProfileOrDeviceOwnerOnCallingUser(context);
259    }
260
261    /**
262     * Checks if {@link android.app.admin.DevicePolicyManager#setAutoTimeRequired} is enforced
263     * on the device.
264     *
265     * @return EnforcedAdmin Object containing the device owner component and
266     * userId the device owner is running as, or {@code null} setAutoTimeRequired is not enforced.
267     */
268    public static EnforcedAdmin checkIfAutoTimeRequired(Context context) {
269        DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
270                Context.DEVICE_POLICY_SERVICE);
271        if (dpm == null || !dpm.getAutoTimeRequired()) {
272            return null;
273        }
274        ComponentName adminComponent = dpm.getDeviceOwnerComponentOnCallingUser();
275        return new EnforcedAdmin(adminComponent, UserHandle.myUserId());
276    }
277
278    /**
279     * Checks if an admin has enforced minimum password quality requirements on the device.
280     *
281     * @return EnforcedAdmin Object containing the enforced admin component and admin user details,
282     * or {@code null} if no quality requirements are set. If the requirements are set by
283     * multiple device admins, then the admin component will be set to {@code null} and userId to
284     * {@link UserHandle#USER_NULL}.
285     *
286     */
287    public static EnforcedAdmin checkIfPasswordQualityIsSet(Context context) {
288        final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
289                Context.DEVICE_POLICY_SERVICE);
290        if (dpm == null) {
291            return null;
292        }
293        boolean isDisabledByMultipleAdmins = false;
294        ComponentName adminComponent = null;
295        List<ComponentName> admins = dpm.getActiveAdmins();
296        int quality;
297        if (admins != null) {
298            for (ComponentName admin : admins) {
299                quality = dpm.getPasswordQuality(admin);
300                if (quality >= DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
301                    if (adminComponent == null) {
302                        adminComponent = admin;
303                    } else {
304                        isDisabledByMultipleAdmins = true;
305                        break;
306                    }
307                }
308            }
309        }
310        EnforcedAdmin enforcedAdmin = null;
311        if (adminComponent != null) {
312            if (!isDisabledByMultipleAdmins) {
313                enforcedAdmin = new EnforcedAdmin(adminComponent, UserHandle.myUserId());
314            } else {
315                enforcedAdmin = new EnforcedAdmin();
316            }
317        }
318        return enforcedAdmin;
319    }
320
321    /**
322     * Checks if any admin has set maximum time to lock.
323     *
324     * @return EnforcedAdmin Object containing the enforced admin component and admin user details,
325     * or {@code null} if no admin has set this restriction. If multiple admins has set this, then
326     * the admin component will be set to {@code null} and userId to {@link UserHandle#USER_NULL}
327     */
328    public static EnforcedAdmin checkIfMaximumTimeToLockIsSet(Context context) {
329        final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
330                Context.DEVICE_POLICY_SERVICE);
331        LockPatternUtils lockPatternUtils = new LockPatternUtils(context);
332        EnforcedAdmin enforcedAdmin = null;
333        final int userId = UserHandle.myUserId();
334        if (lockPatternUtils.isSeparateProfileChallengeEnabled(userId)) {
335            // If the user has a separate challenge, only consider the admins in that user.
336            final List<ComponentName> admins = dpm.getActiveAdminsAsUser(userId);
337            if (admins == null) {
338                return null;
339            }
340            for (ComponentName admin : admins) {
341                if (dpm.getMaximumTimeToLock(admin, userId) > 0) {
342                    if (enforcedAdmin == null) {
343                        enforcedAdmin = new EnforcedAdmin(admin, userId);
344                    } else {
345                        return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN;
346                    }
347                }
348            }
349        } else {
350            // Return all admins for this user and the profiles that are visible from this
351            // user that do not use a separate work challenge.
352            final UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
353            for (UserInfo userInfo : um.getProfiles(userId)) {
354                final List<ComponentName> admins = dpm.getActiveAdminsAsUser(userInfo.id);
355                if (admins == null) {
356                    return null;
357                }
358                final boolean isSeparateProfileChallengeEnabled =
359                        lockPatternUtils.isSeparateProfileChallengeEnabled(userInfo.id);
360                for (ComponentName admin : admins) {
361                    if (!isSeparateProfileChallengeEnabled) {
362                        if (dpm.getMaximumTimeToLock(admin, userInfo.id) > 0) {
363                            if (enforcedAdmin == null) {
364                                enforcedAdmin = new EnforcedAdmin(admin, userInfo.id);
365                            } else {
366                                return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN;
367                            }
368                            // This same admins could have set policies both on the managed profile
369                            // and on the parent. So, if the admin has set the policy on the
370                            // managed profile here, we don't need to further check if that admin
371                            // has set policy on the parent admin.
372                            continue;
373                        }
374                    }
375                    if (userInfo.isManagedProfile()) {
376                        // If userInfo.id is a managed profile, we also need to look at
377                        // the policies set on the parent.
378                        DevicePolicyManager parentDpm = dpm.getParentProfileInstance(admin);
379                        if (parentDpm.getMaximumTimeToLock(admin, userInfo.id) > 0) {
380                            if (enforcedAdmin == null) {
381                                enforcedAdmin = new EnforcedAdmin(admin, userInfo.id);
382                            } else {
383                                return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN;
384                            }
385                        }
386                    }
387                }
388            }
389        }
390        return enforcedAdmin;
391    }
392
393    public static EnforcedAdmin getProfileOrDeviceOwnerOnCallingUser(Context context) {
394        final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
395                Context.DEVICE_POLICY_SERVICE);
396        if (dpm == null) {
397            return null;
398        }
399        ComponentName adminComponent = dpm.getDeviceOwnerComponentOnCallingUser();
400        if (adminComponent != null) {
401            return new EnforcedAdmin(adminComponent, UserHandle.myUserId());
402        }
403        adminComponent = dpm.getProfileOwner();
404        if (adminComponent != null) {
405            return new EnforcedAdmin(adminComponent, UserHandle.myUserId());
406        }
407        return null;
408    }
409
410    /**
411     * Set the menu item as disabled by admin by adding a restricted padlock at the end of the
412     * text and set the click listener which will send an intent to show the admin support details
413     * dialog. If the admin is null, remove the padlock and disabled color span. When the admin is
414     * null, we also set the OnMenuItemClickListener to null, so if you want to set a custom
415     * OnMenuItemClickListener, set it after calling this method.
416     */
417    public static void setMenuItemAsDisabledByAdmin(final Context context,
418            final MenuItem item, final EnforcedAdmin admin) {
419        SpannableStringBuilder sb = new SpannableStringBuilder(item.getTitle());
420        removeExistingRestrictedSpans(sb);
421
422        if (admin != null) {
423            final int disabledColor = context.getColor(R.color.disabled_text_color);
424            sb.setSpan(new ForegroundColorSpan(disabledColor), 0, sb.length(),
425                    Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
426            ImageSpan image = new RestrictedLockImageSpan(context);
427            sb.append(" ", image, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
428
429            item.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
430                @Override
431                public boolean onMenuItemClick(MenuItem item) {
432                    sendShowAdminSupportDetailsIntent(context, admin);
433                    return true;
434                }
435            });
436        } else {
437            item.setOnMenuItemClickListener(null);
438        }
439        item.setTitle(sb);
440    }
441
442    private static void removeExistingRestrictedSpans(SpannableStringBuilder sb) {
443        final int length = sb.length();
444        RestrictedLockImageSpan[] imageSpans = sb.getSpans(length - 1, length,
445                RestrictedLockImageSpan.class);
446        for (ImageSpan span : imageSpans) {
447            final int start = sb.getSpanStart(span);
448            final int end = sb.getSpanEnd(span);
449            sb.removeSpan(span);
450            sb.delete(start, end);
451        }
452        ForegroundColorSpan[] colorSpans = sb.getSpans(0, length, ForegroundColorSpan.class);
453        for (ForegroundColorSpan span : colorSpans) {
454            sb.removeSpan(span);
455        }
456    }
457
458    /**
459     * Send the intent to trigger the {@link android.settings.ShowAdminSupportDetailsDialog}.
460     */
461    public static void sendShowAdminSupportDetailsIntent(Context context, EnforcedAdmin admin) {
462        final Intent intent = getShowAdminSupportDetailsIntent(context, admin);
463        int adminUserId = UserHandle.myUserId();
464        if (admin.userId != UserHandle.USER_NULL) {
465            adminUserId = admin.userId;
466        }
467        context.startActivityAsUser(intent, new UserHandle(adminUserId));
468    }
469
470    public static Intent getShowAdminSupportDetailsIntent(Context context, EnforcedAdmin admin) {
471        final Intent intent = new Intent(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS);
472        if (admin != null) {
473            if (admin.component != null) {
474                intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, admin.component);
475            }
476            int adminUserId = UserHandle.myUserId();
477            if (admin.userId != UserHandle.USER_NULL) {
478                adminUserId = admin.userId;
479            }
480            intent.putExtra(Intent.EXTRA_USER_ID, adminUserId);
481        }
482        return intent;
483    }
484
485    public static void setTextViewPadlock(Context context,
486            TextView textView, boolean showPadlock) {
487        final SpannableStringBuilder sb = new SpannableStringBuilder(textView.getText());
488        removeExistingRestrictedSpans(sb);
489        if (showPadlock) {
490            final ImageSpan image = new RestrictedLockImageSpan(context);
491            sb.append(" ", image, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
492        }
493        textView.setText(sb);
494    }
495
496    /**
497     * Takes a {@link android.widget.TextView} and applies an alpha so that the text looks like
498     * disabled and appends a padlock to the text. This assumes that there are no
499     * ForegroundColorSpans and RestrictedLockImageSpans used on the TextView.
500     */
501    public static void setTextViewAsDisabledByAdmin(Context context,
502            TextView textView, boolean disabled) {
503        final SpannableStringBuilder sb = new SpannableStringBuilder(textView.getText());
504        removeExistingRestrictedSpans(sb);
505        if (disabled) {
506            final int disabledColor = context.getColor(R.color.disabled_text_color);
507            sb.setSpan(new ForegroundColorSpan(disabledColor), 0, sb.length(),
508                    Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
509            final ImageSpan image = new RestrictedLockImageSpan(context);
510            sb.append(" ", image, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
511        }
512        textView.setText(sb);
513    }
514
515    public static class EnforcedAdmin {
516        public ComponentName component = null;
517        public int userId = UserHandle.USER_NULL;
518
519        // We use this to represent the case where a policy is enforced by multiple admins.
520        public final static EnforcedAdmin MULTIPLE_ENFORCED_ADMIN = new EnforcedAdmin();
521
522        public EnforcedAdmin(ComponentName component, int userId) {
523            this.component = component;
524            this.userId = userId;
525        }
526
527        public EnforcedAdmin(EnforcedAdmin other) {
528            if (other == null) {
529                throw new IllegalArgumentException();
530            }
531            this.component = other.component;
532            this.userId = other.userId;
533        }
534
535        public EnforcedAdmin() {}
536
537        @Override
538        public boolean equals(Object object) {
539            if (object == this) return true;
540            if (!(object instanceof EnforcedAdmin)) return false;
541            EnforcedAdmin other = (EnforcedAdmin) object;
542            if (userId != other.userId) {
543                return false;
544            }
545            if ((component == null && other == null) ||
546                    (component != null && component.equals(other))) {
547                return true;
548            }
549            return false;
550        }
551
552        @Override
553        public String toString() {
554            return "EnforcedAdmin{component=" + component + ",userId=" + userId + "}";
555        }
556
557        public void copyTo(EnforcedAdmin other) {
558            if (other == null) {
559                throw new IllegalArgumentException();
560            }
561            other.component = component;
562            other.userId = userId;
563        }
564    }
565}
566