1 /* 2 * Copyright (C) 2021 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.permission.util; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.annotation.UserIdInt; 22 import android.app.admin.DevicePolicyManager; 23 import android.content.Context; 24 import android.os.Binder; 25 import android.os.Process; 26 import android.os.UserHandle; 27 import android.os.UserManager; 28 import android.permission.internal.compat.UserHandleCompat; 29 30 import com.android.internal.util.Preconditions; 31 import com.android.modules.utils.build.SdkLevel; 32 import com.android.permission.flags.Flags; 33 34 import java.util.ArrayList; 35 import java.util.List; 36 37 /** Utility class to deal with Android users. */ 38 public final class UserUtils { 39 UserUtils()40 private UserUtils() {} 41 42 /** Enforces cross user permission for the calling UID and the given {@code userId}. */ enforceCrossUserPermission( @serIdInt int userId, boolean allowAll, boolean enforceForProfileGroup, @NonNull String message, @NonNull Context context)43 public static void enforceCrossUserPermission( 44 @UserIdInt int userId, 45 boolean allowAll, 46 boolean enforceForProfileGroup, 47 @NonNull String message, 48 @NonNull Context context) { 49 final int callingUid = Binder.getCallingUid(); 50 final int callingUserId = UserHandleCompat.getUserId(callingUid); 51 if (userId == callingUserId && !enforceForProfileGroup) { 52 return; 53 } 54 Preconditions.checkArgument( 55 userId >= UserHandleCompat.USER_SYSTEM 56 || (allowAll && userId == UserHandleCompat.USER_ALL), 57 "Invalid user " + userId); 58 context.enforceCallingOrSelfPermission( 59 android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, message); 60 if (callingUid != Process.SHELL_UID || userId == UserHandleCompat.USER_ALL) { 61 return; 62 } 63 64 if (enforceForProfileGroup) { 65 DevicePolicyManager devicePolicyManager = 66 context.getSystemService(DevicePolicyManager.class); 67 if (!devicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile()) { 68 // For profileGroup exclusive roles users on BYOD are free to choose personal or 69 // work profile app regardless of DISALLOW_DEBUGGING_FEATURES 70 return; 71 } 72 73 List<UserHandle> profiles = getUserProfiles(userId, context, true); 74 final int profilesSize = profiles.size(); 75 for (int i = 0; i < profilesSize; i++) { 76 int profileId = profiles.get(i).getIdentifier(); 77 if (profileId == callingUserId) { 78 continue; 79 } 80 enforceShellRestriction(profileId, context); 81 } 82 } else { 83 enforceShellRestriction(userId, context); 84 } 85 } 86 enforceShellRestriction(int userId, @NonNull Context context)87 private static void enforceShellRestriction(int userId, @NonNull Context context) { 88 UserManager userManager = context.getSystemService(UserManager.class); 89 if (userManager.hasUserRestrictionForUser( 90 UserManager.DISALLOW_DEBUGGING_FEATURES, UserHandle.of(userId))) { 91 throw new SecurityException( 92 "Shell does not have permission to access user " + userId); 93 } 94 } 95 96 /** Returns whether a given {@code userId} corresponds to an existing user. */ isUserExistent(@serIdInt int userId, @NonNull Context context)97 public static boolean isUserExistent(@UserIdInt int userId, @NonNull Context context) { 98 return getUserHandles(context).contains(UserHandle.of(userId)); 99 } 100 101 /** Returns all the alive users on the device. */ 102 @NonNull getUserHandles(@onNull Context context)103 public static List<UserHandle> getUserHandles(@NonNull Context context) { 104 UserManager userManager = context.getSystemService(UserManager.class); 105 // This call requires the MANAGE_USERS permission. 106 final long identity = Binder.clearCallingIdentity(); 107 try { 108 return userManager.getUserHandles(true); 109 } finally { 110 Binder.restoreCallingIdentity(identity); 111 } 112 } 113 114 /** Returns all the enabled user profiles on the device for a specified user. */ 115 @NonNull getUserProfiles(@serIdInt int userId, @NonNull Context context)116 public static List<UserHandle> getUserProfiles(@UserIdInt int userId, 117 @NonNull Context context) { 118 return getUserProfiles(userId, context, false); 119 } 120 121 /** 122 * Returns all the enabled user profiles on the device for a specified user 123 * 124 * @param userId the user id to check 125 * @param context the {@link Context} 126 * @param excludePrivate {@code true} to exclude private profiles from returned list of users 127 */ 128 @NonNull getUserProfiles(@serIdInt int userId, @NonNull Context context, boolean excludePrivate)129 public static List<UserHandle> getUserProfiles(@UserIdInt int userId, @NonNull Context context, 130 boolean excludePrivate) { 131 // This call requires the QUERY_USERS permission. 132 final long identity = Binder.clearCallingIdentity(); 133 try { 134 Context userContext = getUserContext(userId, context); 135 UserManager userUserManager = userContext.getSystemService(UserManager.class); 136 List<UserHandle> profiles = userUserManager.getUserProfiles(); 137 if (!excludePrivate) { 138 return profiles; 139 } 140 List<UserHandle> filteredProfiles = new ArrayList<>(); 141 final int profilesSize = profiles.size(); 142 for (int i = 0; i < profilesSize; i++) { 143 UserHandle user = profiles.get(i); 144 if (!isPrivateProfile(user.getIdentifier(), userContext)) { 145 filteredProfiles.add(user); 146 } 147 } 148 return filteredProfiles; 149 } finally { 150 Binder.restoreCallingIdentity(identity); 151 } 152 } 153 154 /** 155 * Returns the parent of a given user, or userId if it has no parent (e.g. it is the primary 156 * profile) 157 */ 158 @UserIdInt getProfileParentIdOrSelf(@serIdInt int userId, @NonNull Context context)159 public static int getProfileParentIdOrSelf(@UserIdInt int userId, @NonNull Context context) { 160 UserHandle profileParent = getProfileParent(userId, context); 161 // If profile parent userhandle is null, then original user id is the parent 162 return profileParent != null ? profileParent.getIdentifier() : userId; 163 } 164 165 /** Returns the parent of a given user. */ 166 @Nullable getProfileParent(@serIdInt int userId, @NonNull Context context)167 private static UserHandle getProfileParent(@UserIdInt int userId, @NonNull Context context) { 168 Context userContext = getUserContext(userId, context); 169 UserManager userUserManager = userContext.getSystemService(UserManager.class); 170 // This call requires the INTERACT_ACROSS_USERS permission. 171 final long identity = Binder.clearCallingIdentity(); 172 try { 173 return userUserManager.getProfileParent(UserHandle.of(userId)); 174 } finally { 175 Binder.restoreCallingIdentity(identity); 176 } 177 } 178 179 /** Returns whether a given {@code userId} corresponds to a managed profile. */ isManagedProfile(@serIdInt int userId, @NonNull Context context)180 public static boolean isManagedProfile(@UserIdInt int userId, @NonNull Context context) { 181 UserManager userManager = context.getSystemService(UserManager.class); 182 // This call requires the QUERY_USERS permission. 183 final long identity = Binder.clearCallingIdentity(); 184 try { 185 return userManager.isManagedProfile(userId); 186 } finally { 187 Binder.restoreCallingIdentity(identity); 188 } 189 } 190 191 /** 192 * Returns whether the given {@code userId} is a private profile. Note that private profiles are 193 * allowed from Android V+ only, so this method will return false on Sdk levels below that. 194 */ isPrivateProfile(@serIdInt int userId, @NonNull Context context)195 public static boolean isPrivateProfile(@UserIdInt int userId, @NonNull Context context) { 196 if (!isPrivateProfileSupported()) { 197 return false; 198 } 199 // It's needed to clear the calling identity because we are going to query the UserManager 200 // for isPrivateProfile() and Context for createContextAsUser, which requires one of the 201 // following permissions: 202 // MANAGE_USERS, QUERY_USERS, or INTERACT_ACROSS_USERS. 203 final long identity = Binder.clearCallingIdentity(); 204 try { 205 Context userContext = getUserContext(userId, context); 206 UserManager userUserManager = userContext.getSystemService(UserManager.class); 207 return userUserManager != null && userUserManager.isPrivateProfile(); 208 } finally { 209 Binder.restoreCallingIdentity(identity); 210 } 211 } 212 213 214 /** 215 * Returns whether private profile's allowed to exist. This can be true iff the SdkLevel is at 216 * least V AND the permission module's private profile feature flag is enabled. 217 */ isPrivateProfileSupported()218 public static boolean isPrivateProfileSupported() { 219 //TODO(b/286539356) add the os feature flag protection when available. 220 return SdkLevel.isAtLeastV() && Flags.privateProfileSupported(); 221 } 222 223 /** 224 * Returns whether a given {@code userId} corresponds to a running managed profile, i.e. the 225 * user is running and the quiet mode is not enabled. 226 */ isProfileRunning(@serIdInt int userId, @NonNull Context context)227 public static boolean isProfileRunning(@UserIdInt int userId, @NonNull Context context) { 228 UserManager userManager = context.getSystemService(UserManager.class); 229 // This call requires the QUERY_USERS permission 230 final long identity = Binder.clearCallingIdentity(); 231 try { 232 return userManager.isUserRunning(UserHandle.of(userId)) 233 && !userManager.isQuietModeEnabled(UserHandle.of(userId)); 234 } finally { 235 Binder.restoreCallingIdentity(identity); 236 } 237 } 238 239 @NonNull getUserContext(@serIdInt int userId, @NonNull Context context)240 public static Context getUserContext(@UserIdInt int userId, @NonNull Context context) { 241 if (SdkLevel.isAtLeastS() && context.getUser().getIdentifier() == userId) { 242 return context; 243 } else { 244 return context.createContextAsUser(UserHandle.of(userId), 0); 245 } 246 } 247 } 248