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