1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.settingslib;
18 
19 import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NONE;
20 import static android.app.admin.DevicePolicyManager.MTE_NOT_CONTROLLED_BY_POLICY;
21 import static android.app.admin.DevicePolicyManager.PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER;
22 import static android.app.role.RoleManager.ROLE_FINANCED_DEVICE_KIOSK;
23 
24 import static com.android.settingslib.Utils.getColorAttrDefaultColor;
25 
26 import android.annotation.UserIdInt;
27 import android.app.AppGlobals;
28 import android.app.AppOpsManager;
29 import android.app.admin.DevicePolicyManager;
30 import android.app.ecm.EnhancedConfirmationManager;
31 import android.app.admin.PackagePolicy;
32 import android.app.role.RoleManager;
33 import android.content.ComponentName;
34 import android.content.Context;
35 import android.content.Intent;
36 import android.content.pm.IPackageManager;
37 import android.content.pm.PackageManager;
38 import android.content.pm.UserInfo;
39 import android.content.res.TypedArray;
40 import android.graphics.drawable.Drawable;
41 import android.os.Build;
42 import android.os.RemoteException;
43 import android.os.UserHandle;
44 import android.os.UserManager;
45 import android.os.UserManager.EnforcingUser;
46 import android.text.SpannableStringBuilder;
47 import android.text.Spanned;
48 import android.text.style.ForegroundColorSpan;
49 import android.text.style.ImageSpan;
50 import android.util.Log;
51 import android.view.MenuItem;
52 import android.widget.TextView;
53 
54 import androidx.annotation.NonNull;
55 import androidx.annotation.Nullable;
56 import androidx.annotation.RequiresApi;
57 import androidx.annotation.VisibleForTesting;
58 
59 import com.android.internal.widget.LockPatternUtils;
60 
61 import java.util.HashSet;
62 import java.util.List;
63 
64 /**
65  * Utility class to host methods usable in adding a restricted padlock icon and showing admin
66  * support message dialog.
67  */
68 public class RestrictedLockUtilsInternal extends RestrictedLockUtils {
69 
70     private static final String LOG_TAG = "RestrictedLockUtils";
71     private static final boolean DEBUG = Log.isLoggable(LOG_TAG, Log.DEBUG);
72 
73     // TODO(b/281701062): reference role name from role manager once its exposed.
74     private static final String ROLE_DEVICE_LOCK_CONTROLLER =
75             "android.app.role.SYSTEM_FINANCED_DEVICE_CONTROLLER";
76 
77     /**
78      * @return drawables for displaying with settings that are locked by a device admin.
79      */
getRestrictedPadlock(Context context)80     public static Drawable getRestrictedPadlock(Context context) {
81         Drawable restrictedPadlock = context.getDrawable(android.R.drawable.ic_info);
82         final int iconSize = context.getResources().getDimensionPixelSize(
83                 android.R.dimen.config_restrictedIconSize);
84 
85         TypedArray ta = context.obtainStyledAttributes(new int[]{android.R.attr.colorAccent});
86         int colorAccent = ta.getColor(0, 0);
87         ta.recycle();
88         restrictedPadlock.setTint(colorAccent);
89 
90         restrictedPadlock.setBounds(0, 0, iconSize, iconSize);
91         return restrictedPadlock;
92     }
93 
94     /**
95      * Checks if a given permission requires additional confirmation for the given package
96      *
97      * @return An intent to show the user if additional confirmation is required, null otherwise
98      */
99     @Nullable
checkIfRequiresEnhancedConfirmation(@onNull Context context, @NonNull String settingIdentifier, @NonNull String packageName)100     public static Intent checkIfRequiresEnhancedConfirmation(@NonNull Context context,
101             @NonNull String settingIdentifier, @NonNull String packageName) {
102 
103         if (!android.permission.flags.Flags.enhancedConfirmationModeApisEnabled()
104                 || !android.security.Flags.extendEcmToAllSettings()) {
105             return null;
106         }
107 
108         EnhancedConfirmationManager ecManager = (EnhancedConfirmationManager) context
109                 .getSystemService(Context.ECM_ENHANCED_CONFIRMATION_SERVICE);
110         try {
111             if (ecManager.isRestricted(packageName, settingIdentifier)) {
112                 return ecManager.createRestrictedSettingDialogIntent(
113                         packageName, settingIdentifier);
114             }
115         } catch (PackageManager.NameNotFoundException e) {
116             Log.e(LOG_TAG, "package not found: " + packageName, e);
117         }
118 
119         return null;
120     }
121 
122     /**
123      * <p>This is {@code true} when the setting is a protected setting (i.e., a sensitive resource),
124      * and the app is restricted (i.e., considered dangerous), and the user has not yet cleared the
125      * app's restriction status (i.e., by clicking "Allow restricted settings" for this app).     *
126      */
isEnhancedConfirmationRestricted(@onNull Context context, @NonNull String settingIdentifier, @NonNull String packageName)127     public static boolean isEnhancedConfirmationRestricted(@NonNull Context context,
128             @NonNull String settingIdentifier, @NonNull String packageName) {
129         if (android.permission.flags.Flags.enhancedConfirmationModeApisEnabled()
130                 && android.security.Flags.extendEcmToAllSettings()) {
131             try {
132                 return context.getSystemService(EnhancedConfirmationManager.class)
133                         .isRestricted(packageName, settingIdentifier);
134             } catch (PackageManager.NameNotFoundException e) {
135                 Log.e(LOG_TAG, "Exception when retrieving package:" + packageName, e);
136                 return false;
137             }
138         } else {
139             try {
140                 if (!settingIdentifier.equals(AppOpsManager.OPSTR_BIND_ACCESSIBILITY_SERVICE)) {
141                     return false;
142                 }
143                 int uid = context.getPackageManager().getPackageUid(packageName, 0);
144                 final int mode = context.getSystemService(AppOpsManager.class)
145                         .noteOpNoThrow(AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS,
146                         uid, packageName);
147                 final boolean ecmEnabled = context.getResources().getBoolean(
148                         com.android.internal.R.bool.config_enhancedConfirmationModeEnabled);
149                 return ecmEnabled && mode != AppOpsManager.MODE_ALLOWED
150                         && mode != AppOpsManager.MODE_DEFAULT;
151             } catch (Exception e) {
152                 // Fallback in case if app ops is not available in testing.
153                 return false;
154             }
155         }
156     }
157 
158     /**
159      * Checks if a restriction is enforced on a user and returns the enforced admin and
160      * admin userId.
161      *
162      * @param userRestriction Restriction to check
163      * @param userId User which we need to check if restriction is enforced on.
164      * @return EnforcedAdmin Object containing the enforced admin component and admin user details,
165      * or {@code null} If the restriction is not set. If the restriction is set by both device owner
166      * and profile owner, then the admin component will be set to {@code null} and userId to
167      * {@link UserHandle#USER_NULL}.
168      */
checkIfRestrictionEnforced(Context context, String userRestriction, int userId)169     public static EnforcedAdmin checkIfRestrictionEnforced(Context context,
170             String userRestriction, int userId) {
171         final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
172                 Context.DEVICE_POLICY_SERVICE);
173         if (dpm == null) {
174             return null;
175         }
176 
177         final UserManager um = UserManager.get(context);
178         final UserHandle userHandle = UserHandle.of(userId);
179         final List<UserManager.EnforcingUser> enforcingUsers =
180                 um.getUserRestrictionSources(userRestriction, userHandle);
181 
182         if (enforcingUsers.isEmpty()) {
183             // Restriction is not enforced.
184             return null;
185         }
186         final int size = enforcingUsers.size();
187         if (size > 1) {
188             final EnforcedAdmin enforcedAdmin = EnforcedAdmin
189                     .createDefaultEnforcedAdminWithRestriction(userRestriction);
190             enforcedAdmin.user = userHandle;
191             if (DEBUG) {
192                 Log.d(LOG_TAG, "Multiple (" + size + ") enforcing users for restriction '"
193                         + userRestriction + "' on user " + userHandle + "; returning default admin "
194                         + "(" + enforcedAdmin + ")");
195             }
196             return enforcedAdmin;
197         }
198 
199         final EnforcingUser enforcingUser = enforcingUsers.get(0);
200         final int restrictionSource = enforcingUser.getUserRestrictionSource();
201         if (restrictionSource == UserManager.RESTRICTION_SOURCE_SYSTEM) {
202             return null;
203         }
204 
205         final EnforcedAdmin admin =
206                 getProfileOrDeviceOwner(context, userRestriction, enforcingUser.getUserHandle());
207         if (admin != null) {
208             return admin;
209         }
210         return EnforcedAdmin.createDefaultEnforcedAdminWithRestriction(userRestriction);
211     }
212 
hasBaseUserRestriction(Context context, String userRestriction, int userId)213     public static boolean hasBaseUserRestriction(Context context,
214             String userRestriction, int userId) {
215         final UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
216         return um.hasBaseUserRestriction(userRestriction, UserHandle.of(userId));
217     }
218 
219     /**
220      * Checks whether keyguard features are disabled by policy.
221      *
222      * @param context {@link Context} for the calling user.
223      *
224      * @param keyguardFeatures Any one of keyguard features that can be
225      * disabled by {@link android.app.admin.DevicePolicyManager#setKeyguardDisabledFeatures}.
226      *
227      * @param userId User to check enforced admin status for.
228      *
229      * @return EnforcedAdmin Object containing the enforced admin component and admin user details,
230      * or {@code null} If the notification features are not disabled. If the restriction is set by
231      * multiple admins, then the admin component will be set to {@code null} and userId to
232      * {@link UserHandle#USER_NULL}.
233      */
checkIfKeyguardFeaturesDisabled(Context context, int keyguardFeatures, final @UserIdInt int userId)234     public static EnforcedAdmin checkIfKeyguardFeaturesDisabled(Context context,
235             int keyguardFeatures, final @UserIdInt int userId) {
236         final LockSettingCheck check = (dpm, admin, checkUser) -> {
237             int effectiveFeatures = dpm.getKeyguardDisabledFeatures(admin, checkUser);
238             if (checkUser != userId) {
239                 effectiveFeatures &= PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER;
240             }
241             return (effectiveFeatures & keyguardFeatures) != KEYGUARD_DISABLE_FEATURES_NONE;
242         };
243         if (UserManager.get(context).getUserInfo(userId).isManagedProfile()) {
244             DevicePolicyManager dpm =
245                     (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
246             return findEnforcedAdmin(dpm.getActiveAdminsAsUser(userId), dpm, userId, check);
247         }
248         return checkForLockSetting(context, userId, check);
249     }
250 
251     /**
252      * @return the UserHandle for a userId. Return null for USER_NULL
253      */
getUserHandleOf(@serIdInt int userId)254     private static UserHandle getUserHandleOf(@UserIdInt int userId) {
255         if (userId == UserHandle.USER_NULL) {
256             return null;
257         } else {
258             return UserHandle.of(userId);
259         }
260     }
261 
262     /**
263      * Filter a set of device admins based on a predicate {@code check}. This is equivalent to
264      * {@code admins.stream().filter(check).map(x → new EnforcedAdmin(admin, userId)} except it's
265      * returning a zero/one/many-type thing.
266      *
267      * @param admins set of candidate device admins identified by {@link ComponentName}.
268      * @param userId user to create the resultant {@link EnforcedAdmin} as.
269      * @param check filter predicate.
270      *
271      * @return {@code null} if none of the {@param admins} match.
272      *         An {@link EnforcedAdmin} if exactly one of the admins matches.
273      *         Otherwise, {@link EnforcedAdmin#MULTIPLE_ENFORCED_ADMIN} for multiple matches.
274      */
275     @Nullable
findEnforcedAdmin(@ullable List<ComponentName> admins, @NonNull DevicePolicyManager dpm, @UserIdInt int userId, @NonNull LockSettingCheck check)276     private static EnforcedAdmin findEnforcedAdmin(@Nullable List<ComponentName> admins,
277             @NonNull DevicePolicyManager dpm, @UserIdInt int userId,
278             @NonNull LockSettingCheck check) {
279         if (admins == null) {
280             return null;
281         }
282 
283         final UserHandle user = getUserHandleOf(userId);
284         EnforcedAdmin enforcedAdmin = null;
285         for (ComponentName admin : admins) {
286             if (check.isEnforcing(dpm, admin, userId)) {
287                 if (enforcedAdmin == null) {
288                     enforcedAdmin = new EnforcedAdmin(admin, user);
289                 } else {
290                     return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN;
291                 }
292             }
293         }
294         return enforcedAdmin;
295     }
296 
checkIfUninstallBlocked(Context context, String packageName, int userId)297     public static EnforcedAdmin checkIfUninstallBlocked(Context context,
298             String packageName, int userId) {
299         EnforcedAdmin allAppsControlDisallowedAdmin = checkIfRestrictionEnforced(context,
300                 UserManager.DISALLOW_APPS_CONTROL, userId);
301         if (allAppsControlDisallowedAdmin != null) {
302             return allAppsControlDisallowedAdmin;
303         }
304         EnforcedAdmin allAppsUninstallDisallowedAdmin = checkIfRestrictionEnforced(context,
305                 UserManager.DISALLOW_UNINSTALL_APPS, userId);
306         if (allAppsUninstallDisallowedAdmin != null) {
307             return allAppsUninstallDisallowedAdmin;
308         }
309         IPackageManager ipm = AppGlobals.getPackageManager();
310         try {
311             if (ipm.getBlockUninstallForUser(packageName, userId)) {
312                 return getProfileOrDeviceOwner(context, getUserHandleOf(userId));
313             }
314         } catch (RemoteException e) {
315             // Nothing to do
316         }
317         return null;
318     }
319 
320     /**
321      * Check if an application is suspended.
322      *
323      * @return EnforcedAdmin Object containing the enforced admin component and admin user details,
324      * or {@code null} if the application is not suspended.
325      */
checkIfApplicationIsSuspended(Context context, String packageName, int userId)326     public static EnforcedAdmin checkIfApplicationIsSuspended(Context context, String packageName,
327             int userId) {
328         IPackageManager ipm = AppGlobals.getPackageManager();
329         try {
330             if (ipm.isPackageSuspendedForUser(packageName, userId)) {
331                 return getProfileOrDeviceOwner(context, getUserHandleOf(userId));
332             }
333         } catch (RemoteException | IllegalArgumentException e) {
334             // Nothing to do
335         }
336         return null;
337     }
338 
checkIfInputMethodDisallowed(Context context, String packageName, int userId)339     public static EnforcedAdmin checkIfInputMethodDisallowed(Context context,
340             String packageName, int userId) {
341         DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
342                 Context.DEVICE_POLICY_SERVICE);
343         if (dpm == null) {
344             return null;
345         }
346         EnforcedAdmin admin = getProfileOrDeviceOwner(context, getUserHandleOf(userId));
347         boolean permitted = true;
348         if (admin != null) {
349             permitted = dpm.isInputMethodPermittedByAdmin(admin.component,
350                     packageName, userId);
351         }
352 
353         boolean permittedByParentAdmin = true;
354         EnforcedAdmin profileAdmin = null;
355         int managedProfileId = getManagedProfileId(context, userId);
356         if (managedProfileId != UserHandle.USER_NULL) {
357             profileAdmin = getProfileOrDeviceOwner(context, getUserHandleOf(managedProfileId));
358             // If the device is an organization-owned device with a managed profile, the
359             // managedProfileId will be used instead of the affected userId. This is because
360             // isInputMethodPermittedByAdmin is called on the parent DPM instance, which will
361             // return results affecting the personal profile.
362             if (profileAdmin != null && dpm.isOrganizationOwnedDeviceWithManagedProfile()) {
363                 DevicePolicyManager parentDpm = sProxy.getParentProfileInstance(dpm,
364                         UserManager.get(context).getUserInfo(managedProfileId));
365                 permittedByParentAdmin = parentDpm.isInputMethodPermittedByAdmin(
366                         profileAdmin.component, packageName, managedProfileId);
367             }
368         }
369         if (!permitted && !permittedByParentAdmin) {
370             return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN;
371         } else if (!permitted) {
372             return admin;
373         } else if (!permittedByParentAdmin) {
374             return profileAdmin;
375         }
376         return null;
377     }
378 
379     /**
380      * @param context
381      * @param userId user id of a managed profile.
382      * @return is remote contacts search disallowed.
383      */
checkIfRemoteContactSearchDisallowed(Context context, int userId)384     public static EnforcedAdmin checkIfRemoteContactSearchDisallowed(Context context, int userId) {
385         DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
386                 Context.DEVICE_POLICY_SERVICE);
387         if (dpm == null) {
388             return null;
389         }
390         EnforcedAdmin admin = getProfileOwner(context, userId);
391         if (admin == null) {
392             return null;
393         }
394         UserHandle userHandle = UserHandle.of(userId);
395         if (dpm.getCrossProfileContactsSearchDisabled(userHandle)
396                 && dpm.getCrossProfileCallerIdDisabled(userHandle)) {
397             return admin;
398         }
399         return null;
400     }
401 
checkIfAccessibilityServiceDisallowed(Context context, String packageName, int userId)402     public static EnforcedAdmin checkIfAccessibilityServiceDisallowed(Context context,
403             String packageName, int userId) {
404         DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
405                 Context.DEVICE_POLICY_SERVICE);
406         if (dpm == null) {
407             return null;
408         }
409         EnforcedAdmin admin = getProfileOrDeviceOwner(context, getUserHandleOf(userId));
410         boolean permitted = true;
411         if (admin != null) {
412             permitted = dpm.isAccessibilityServicePermittedByAdmin(admin.component,
413                     packageName, userId);
414         }
415         int managedProfileId = getManagedProfileId(context, userId);
416         EnforcedAdmin profileAdmin = getProfileOrDeviceOwner(context,
417                 getUserHandleOf(managedProfileId));
418         boolean permittedByProfileAdmin = true;
419         if (profileAdmin != null) {
420             permittedByProfileAdmin = dpm.isAccessibilityServicePermittedByAdmin(
421                     profileAdmin.component, packageName, managedProfileId);
422         }
423         if (!permitted && !permittedByProfileAdmin) {
424             return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN;
425         } else if (!permitted) {
426             return admin;
427         } else if (!permittedByProfileAdmin) {
428             return profileAdmin;
429         }
430         return null;
431     }
432 
getManagedProfileId(Context context, int userId)433     private static int getManagedProfileId(Context context, int userId) {
434         UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
435         List<UserInfo> userProfiles = um.getProfiles(userId);
436         for (UserInfo uInfo : userProfiles) {
437             if (uInfo.id == userId) {
438                 continue;
439             }
440             if (uInfo.isManagedProfile()) {
441                 return uInfo.id;
442             }
443         }
444         return UserHandle.USER_NULL;
445     }
446 
447     /**
448      * Check if account management for a specific type of account is disabled by admin.
449      * Only a profile or device owner can disable account management. So, we check if account
450      * management is disabled and return profile or device owner on the calling user.
451      *
452      * @return EnforcedAdmin Object containing the enforced admin component and admin user details,
453      * or {@code null} if the account management is not disabled.
454      */
checkIfAccountManagementDisabled(Context context, String accountType, int userId)455     public static EnforcedAdmin checkIfAccountManagementDisabled(Context context,
456             String accountType, int userId) {
457         if (accountType == null) {
458             return null;
459         }
460         DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
461                 Context.DEVICE_POLICY_SERVICE);
462         PackageManager pm = context.getPackageManager();
463         if (!pm.hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN) || dpm == null) {
464             return null;
465         }
466         boolean isAccountTypeDisabled = false;
467         String[] disabledTypes = dpm.getAccountTypesWithManagementDisabledAsUser(userId);
468         for (String type : disabledTypes) {
469             if (accountType.equals(type)) {
470                 isAccountTypeDisabled = true;
471                 break;
472             }
473         }
474         if (!isAccountTypeDisabled) {
475             return null;
476         }
477         return getProfileOrDeviceOwner(context, getUserHandleOf(userId));
478     }
479 
480     /**
481      * Check if USB data signaling (except from charging functions) is disabled by the admin.
482      * Only a device owner or a profile owner on an organization-owned managed profile can disable
483      * USB data signaling.
484      *
485      * @return EnforcedAdmin Object containing the enforced admin component and admin user details,
486      * or {@code null} if USB data signaling is not disabled.
487      */
checkIfUsbDataSignalingIsDisabled(Context context, int userId)488     public static EnforcedAdmin checkIfUsbDataSignalingIsDisabled(Context context, int userId) {
489         DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
490         if (dpm == null || dpm.isUsbDataSignalingEnabled()) {
491             return null;
492         } else {
493             EnforcedAdmin admin = getProfileOrDeviceOwner(context, getUserHandleOf(userId));
494             int managedProfileId = getManagedProfileId(context, userId);
495             if (admin == null && managedProfileId != UserHandle.USER_NULL) {
496                 admin = getProfileOrDeviceOwner(context, getUserHandleOf(managedProfileId));
497             }
498             return admin;
499         }
500     }
501 
502     /**
503      * Check if user control over metered data usage of {@code packageName} is disabled by the
504      * profile or device owner.
505      *
506      * @return EnforcedAdmin object containing the enforced admin component and admin user details,
507      * or {@code null} if the user control is not disabled.
508      */
checkIfMeteredDataUsageUserControlDisabled(Context context, String packageName, int userId)509     public static EnforcedAdmin checkIfMeteredDataUsageUserControlDisabled(Context context,
510             String packageName, int userId) {
511         RoleManager roleManager = context.getSystemService(RoleManager.class);
512         UserHandle userHandle = getUserHandleOf(userId);
513         if (roleManager.getRoleHoldersAsUser(ROLE_FINANCED_DEVICE_KIOSK, userHandle)
514                 .contains(packageName)
515                 || roleManager.getRoleHoldersAsUser(ROLE_DEVICE_LOCK_CONTROLLER, userHandle)
516                 .contains(packageName)) {
517             // There is no actual device admin for a financed device, but metered data usage
518             // control should still be disabled for both controller and kiosk apps.
519             return new EnforcedAdmin();
520         }
521 
522         final EnforcedAdmin enforcedAdmin = getProfileOrDeviceOwner(context,
523                 userHandle);
524         if (enforcedAdmin == null) {
525             return null;
526         }
527 
528         final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
529                 Context.DEVICE_POLICY_SERVICE);
530         return dpm.isMeteredDataDisabledPackageForUser(enforcedAdmin.component, packageName, userId)
531                 ? enforcedAdmin : null;
532     }
533 
534     /**
535      * Checks if an admin has enforced minimum password quality or complexity requirements on the
536      * given user.
537      *
538      * @return EnforcedAdmin Object containing the enforced admin component and admin user details,
539      * or {@code null} if no quality requirements are set. If the requirements are set by
540      * multiple device admins, then the admin component will be set to {@code null} and userId to
541      * {@link UserHandle#USER_NULL}.
542      */
checkIfPasswordQualityIsSet(Context context, int userId)543     public static EnforcedAdmin checkIfPasswordQualityIsSet(Context context, int userId) {
544         final LockSettingCheck check =
545                 (DevicePolicyManager dpm, ComponentName admin, @UserIdInt int checkUser) ->
546                         dpm.getPasswordQuality(admin, checkUser)
547                                 > DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
548 
549         final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
550                 Context.DEVICE_POLICY_SERVICE);
551         if (dpm == null) {
552             return null;
553         }
554 
555         LockPatternUtils lockPatternUtils = new LockPatternUtils(context);
556         final int aggregatedComplexity = dpm.getAggregatedPasswordComplexityForUser(userId);
557         if (aggregatedComplexity > DevicePolicyManager.PASSWORD_COMPLEXITY_NONE) {
558             // First, check if there's a Device Owner. If so, then only it can apply password
559             // complexity requiremnts (there can be no secondary profiles).
560             final UserHandle deviceOwnerUser = dpm.getDeviceOwnerUser();
561             if (deviceOwnerUser != null) {
562                 return new EnforcedAdmin(dpm.getDeviceOwnerComponentOnAnyUser(), deviceOwnerUser);
563             }
564 
565             // The complexity could be enforced by a Profile Owner - either in the current user
566             // or the current user is the parent user that is affected by the profile owner.
567             for (UserInfo userInfo : UserManager.get(context).getProfiles(userId)) {
568                 final ComponentName profileOwnerComponent = dpm.getProfileOwnerAsUser(userInfo.id);
569                 if (profileOwnerComponent != null) {
570                     return new EnforcedAdmin(profileOwnerComponent, getUserHandleOf(userInfo.id));
571                 }
572             }
573 
574             // Should not get here: A Device Owner or Profile Owner should be found.
575             throw new IllegalStateException(
576                     String.format("Could not find admin enforcing complexity %d for user %d",
577                             aggregatedComplexity, userId));
578         }
579 
580         if (sProxy.isSeparateProfileChallengeEnabled(lockPatternUtils, userId)) {
581             // userId is managed profile and has a separate challenge, only consider
582             // the admins in that user.
583             final List<ComponentName> admins = dpm.getActiveAdminsAsUser(userId);
584             if (admins == null) {
585                 return null;
586             }
587             EnforcedAdmin enforcedAdmin = null;
588             final UserHandle user = getUserHandleOf(userId);
589             for (ComponentName admin : admins) {
590                 if (check.isEnforcing(dpm, admin, userId)) {
591                     if (enforcedAdmin == null) {
592                         enforcedAdmin = new EnforcedAdmin(admin, user);
593                     } else {
594                         return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN;
595                     }
596                 }
597             }
598             return enforcedAdmin;
599         } else {
600             return checkForLockSetting(context, userId, check);
601         }
602     }
603 
604     /**
605      * Checks if any admin has set maximum time to lock.
606      *
607      * @return EnforcedAdmin Object containing the enforced admin component and admin user details,
608      * or {@code null} if no admin has set this restriction. If multiple admins has set this, then
609      * the admin component will be set to {@code null} and userId to {@link UserHandle#USER_NULL}
610      */
checkIfMaximumTimeToLockIsSet(Context context)611     public static EnforcedAdmin checkIfMaximumTimeToLockIsSet(Context context) {
612         return checkForLockSetting(context, UserHandle.myUserId(),
613                 (DevicePolicyManager dpm, ComponentName admin, @UserIdInt int userId) ->
614                         dpm.getMaximumTimeToLock(admin, userId) > 0);
615     }
616 
617     private interface LockSettingCheck {
isEnforcing(DevicePolicyManager dpm, ComponentName admin, @UserIdInt int userId)618         boolean isEnforcing(DevicePolicyManager dpm, ComponentName admin, @UserIdInt int userId);
619     }
620 
621     /**
622      * Checks whether any of the user's profiles enforce the lock setting. A managed profile is only
623      * included if it does not have a separate challenge.
624      *
625      * The user identified by {@param userId} is always included.
626      */
checkForLockSetting( Context context, @UserIdInt int userId, LockSettingCheck check)627     private static EnforcedAdmin checkForLockSetting(
628             Context context, @UserIdInt int userId, LockSettingCheck check) {
629         final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
630                 Context.DEVICE_POLICY_SERVICE);
631         if (dpm == null) {
632             return null;
633         }
634         final LockPatternUtils lockPatternUtils = new LockPatternUtils(context);
635         EnforcedAdmin enforcedAdmin = null;
636         // Return all admins for this user and the profiles that are visible from this
637         // user that do not use a separate work challenge.
638         for (UserInfo userInfo : UserManager.get(context).getProfiles(userId)) {
639             final List<ComponentName> admins = dpm.getActiveAdminsAsUser(userInfo.id);
640             if (admins == null) {
641                 continue;
642             }
643             final UserHandle user = getUserHandleOf(userInfo.id);
644             final boolean isSeparateProfileChallengeEnabled =
645                     sProxy.isSeparateProfileChallengeEnabled(lockPatternUtils, userInfo.id);
646             for (ComponentName admin : admins) {
647                 if (!isSeparateProfileChallengeEnabled) {
648                     if (check.isEnforcing(dpm, admin, userInfo.id)) {
649                         if (enforcedAdmin == null) {
650                             enforcedAdmin = new EnforcedAdmin(admin, user);
651                         } else {
652                             return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN;
653                         }
654                         // This same admins could have set policies both on the managed profile
655                         // and on the parent. So, if the admin has set the policy on the
656                         // managed profile here, we don't need to further check if that admin
657                         // has set policy on the parent admin.
658                         continue;
659                     }
660                 }
661                 if (userInfo.isManagedProfile()) {
662                     // If userInfo.id is a managed profile, we also need to look at
663                     // the policies set on the parent.
664                     DevicePolicyManager parentDpm = sProxy.getParentProfileInstance(dpm, userInfo);
665                     if (check.isEnforcing(parentDpm, admin, userInfo.id)) {
666                         if (enforcedAdmin == null) {
667                             enforcedAdmin = new EnforcedAdmin(admin, user);
668                         } else {
669                             return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN;
670                         }
671                     }
672                 }
673             }
674         }
675         return enforcedAdmin;
676     }
677 
getDeviceOwner(Context context)678     public static EnforcedAdmin getDeviceOwner(Context context) {
679         return getDeviceOwner(context, null);
680     }
681 
getDeviceOwner(Context context, String enforcedRestriction)682     private static EnforcedAdmin getDeviceOwner(Context context, String enforcedRestriction) {
683         final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
684                 Context.DEVICE_POLICY_SERVICE);
685         if (dpm == null) {
686             return null;
687         }
688         ComponentName adminComponent = dpm.getDeviceOwnerComponentOnAnyUser();
689         if (adminComponent != null) {
690             return new EnforcedAdmin(
691                     adminComponent, enforcedRestriction, dpm.getDeviceOwnerUser());
692         }
693         return null;
694     }
695 
getProfileOwner(Context context, int userId)696     private static EnforcedAdmin getProfileOwner(Context context, int userId) {
697         return getProfileOwner(context, null, userId);
698     }
699 
getProfileOwner( Context context, String enforcedRestriction, int userId)700     private static EnforcedAdmin getProfileOwner(
701             Context context, String enforcedRestriction, int userId) {
702         if (userId == UserHandle.USER_NULL) {
703             return null;
704         }
705         final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
706                 Context.DEVICE_POLICY_SERVICE);
707         if (dpm == null) {
708             return null;
709         }
710         ComponentName adminComponent = dpm.getProfileOwnerAsUser(userId);
711         if (adminComponent != null) {
712             return new EnforcedAdmin(adminComponent, enforcedRestriction, getUserHandleOf(userId));
713         }
714         return null;
715     }
716 
717     /**
718      * Set the menu item as disabled by admin by adding a restricted padlock at the end of the
719      * text and set the click listener which will send an intent to show the admin support details
720      * dialog. If the admin is null, remove the padlock and disabled color span. When the admin is
721      * null, we also set the OnMenuItemClickListener to null, so if you want to set a custom
722      * OnMenuItemClickListener, set it after calling this method.
723      */
setMenuItemAsDisabledByAdmin(final Context context, final MenuItem item, final EnforcedAdmin admin)724     public static void setMenuItemAsDisabledByAdmin(final Context context,
725             final MenuItem item, final EnforcedAdmin admin) {
726         SpannableStringBuilder sb = new SpannableStringBuilder(item.getTitle());
727         removeExistingRestrictedSpans(sb);
728 
729         if (admin != null) {
730             final int disabledColor = getColorAttrDefaultColor(context,
731                     android.R.attr.textColorHint);
732             sb.setSpan(new ForegroundColorSpan(disabledColor), 0, sb.length(),
733                     Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
734             ImageSpan image = new RestrictedLockImageSpan(context);
735             sb.append(" ", image, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
736 
737             item.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
738                 @Override
739                 public boolean onMenuItemClick(MenuItem item) {
740                     sendShowAdminSupportDetailsIntent(context, admin);
741                     return true;
742                 }
743             });
744         } else {
745             item.setOnMenuItemClickListener(null);
746         }
747         item.setTitle(sb);
748     }
749 
removeExistingRestrictedSpans(SpannableStringBuilder sb)750     private static void removeExistingRestrictedSpans(SpannableStringBuilder sb) {
751         final int length = sb.length();
752         RestrictedLockImageSpan[] imageSpans = sb.getSpans(length - 1, length,
753                 RestrictedLockImageSpan.class);
754         for (ImageSpan span : imageSpans) {
755             final int start = sb.getSpanStart(span);
756             final int end = sb.getSpanEnd(span);
757             sb.removeSpan(span);
758             sb.delete(start, end);
759         }
760         ForegroundColorSpan[] colorSpans = sb.getSpans(0, length, ForegroundColorSpan.class);
761         for (ForegroundColorSpan span : colorSpans) {
762             sb.removeSpan(span);
763         }
764     }
765 
isAdminInCurrentUserOrProfile(Context context, ComponentName admin)766     public static boolean isAdminInCurrentUserOrProfile(Context context, ComponentName admin) {
767         DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
768                 Context.DEVICE_POLICY_SERVICE);
769         UserManager um = UserManager.get(context);
770         for (UserInfo userInfo : um.getProfiles(UserHandle.myUserId())) {
771             if (dpm.isAdminActiveAsUser(admin, userInfo.id)) {
772                 return true;
773             }
774         }
775         return false;
776     }
777 
setTextViewPadlock(Context context, TextView textView, boolean showPadlock)778     public static void setTextViewPadlock(Context context,
779             TextView textView, boolean showPadlock) {
780         final SpannableStringBuilder sb = new SpannableStringBuilder(textView.getText());
781         removeExistingRestrictedSpans(sb);
782         if (showPadlock) {
783             final ImageSpan image = new RestrictedLockImageSpan(context);
784             sb.append(" ", image, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
785         }
786         textView.setText(sb);
787     }
788 
789     /**
790      * Takes a {@link android.widget.TextView} and applies an alpha so that the text looks like
791      * disabled and appends a padlock to the text. This assumes that there are no
792      * ForegroundColorSpans and RestrictedLockImageSpans used on the TextView.
793      */
setTextViewAsDisabledByAdmin(Context context, TextView textView, boolean disabled)794     public static void setTextViewAsDisabledByAdmin(Context context,
795             TextView textView, boolean disabled) {
796         final SpannableStringBuilder sb = new SpannableStringBuilder(textView.getText());
797         removeExistingRestrictedSpans(sb);
798         if (disabled) {
799             final int disabledColor = Utils.getDisabled(context,
800                     textView.getCurrentTextColor());
801             sb.setSpan(new ForegroundColorSpan(disabledColor), 0, sb.length(),
802                     Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
803             textView.setCompoundDrawables(null, null, getRestrictedPadlock(context), null);
804             textView.setCompoundDrawablePadding(context.getResources().getDimensionPixelSize(
805                     R.dimen.restricted_icon_padding));
806         } else {
807             textView.setCompoundDrawables(null, null, null, null);
808         }
809         textView.setText(sb);
810     }
811 
812     /**
813      * Checks whether MTE (Advanced memory protection) controls are disabled by the enterprise
814      * policy.
815      */
816     @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
checkIfMteIsDisabled(Context context)817     public static EnforcedAdmin checkIfMteIsDisabled(Context context) {
818         final DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
819         if (dpm.getMtePolicy() == MTE_NOT_CONTROLLED_BY_POLICY) {
820             return null;
821         }
822         EnforcedAdmin admin =
823                 RestrictedLockUtils.getProfileOrDeviceOwner(
824                         context, UserHandle.of(UserHandle.USER_SYSTEM));
825         if (admin != null) {
826             return admin;
827         }
828         int profileId = getManagedProfileId(context, UserHandle.USER_SYSTEM);
829         return RestrictedLockUtils.getProfileOrDeviceOwner(context, UserHandle.of(profileId));
830     }
831 
832     /**
833      * Check if there are restrictions on an application from being a Credential Manager provider.
834      *
835      * @return EnforcedAdmin Object containing the enforced admin component and admin user details,
836      * or {@code null} if the setting is not managed.
837      */
checkIfApplicationCanBeCredentialManagerProvider( @onNull Context context, @NonNull String packageName)838     public static @Nullable EnforcedAdmin checkIfApplicationCanBeCredentialManagerProvider(
839             @NonNull Context context, @NonNull String packageName) {
840         final DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
841         final PackagePolicy pp = dpm.getCredentialManagerPolicy();
842 
843         if (pp == null || pp.isPackageAllowed(packageName, new HashSet<>())) {
844             return null;
845         }
846 
847         EnforcedAdmin admin = RestrictedLockUtilsInternal.getDeviceOwner(context);
848         if (admin != null) {
849             return admin;
850         }
851         int profileId = getManagedProfileId(context, UserHandle.USER_SYSTEM);
852         return RestrictedLockUtils.getProfileOrDeviceOwner(context, UserHandle.of(profileId));
853     }
854 
855     /**
856      * Static {@link LockPatternUtils} and {@link DevicePolicyManager} wrapper for testing purposes.
857      * {@link LockPatternUtils} is an internal API not supported by robolectric.
858      * {@link DevicePolicyManager} has a {@code getProfileParent} not yet suppored by robolectric.
859      */
860     @VisibleForTesting
861     static Proxy sProxy = new Proxy();
862 
863     @VisibleForTesting
864     static class Proxy {
isSeparateProfileChallengeEnabled(LockPatternUtils utils, int userHandle)865         public boolean isSeparateProfileChallengeEnabled(LockPatternUtils utils, int userHandle) {
866             return utils.isSeparateProfileChallengeEnabled(userHandle);
867         }
868 
getParentProfileInstance(DevicePolicyManager dpm, UserInfo ui)869         public DevicePolicyManager getParentProfileInstance(DevicePolicyManager dpm, UserInfo ui) {
870             return dpm.getParentProfileInstance(ui);
871         }
872     }
873 }
874