1 /* 2 * Copyright (C) 2022 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.safetycenter; 18 19 import static java.util.Objects.requireNonNull; 20 21 import android.annotation.IntDef; 22 import android.annotation.UserIdInt; 23 import android.content.Context; 24 import android.os.Binder; 25 import android.os.UserHandle; 26 import android.os.UserManager; 27 import android.permission.internal.compat.UserHandleCompat; 28 import android.util.Log; 29 30 import com.android.permission.util.UserUtils; 31 32 import java.lang.annotation.Retention; 33 import java.lang.annotation.RetentionPolicy; 34 import java.util.ArrayList; 35 import java.util.Arrays; 36 import java.util.List; 37 import java.util.Objects; 38 39 /** 40 * A class that represent all the enabled profiles (profile parent and managed profile(s)) 41 * associated with a user id. 42 * 43 * @hide 44 */ 45 //TODO(b/286539356) Do not expose the private profile when it's not running. 46 public final class UserProfileGroup { 47 48 private static final String TAG = "UserProfileGroup"; 49 50 @UserIdInt private final int mProfileParentUserId; 51 private final int[] mManagedProfilesUserIds; 52 private final int[] mManagedRunningProfilesUserIds; 53 54 @UserIdInt private final int mPrivateProfileUserId; 55 private final boolean mPrivateProfileRunning; 56 57 /** Respresents the profile type of the primary user. */ 58 public static final int PROFILE_TYPE_PRIMARY = 0; 59 /** Respresents the profile type of the managed profile. */ 60 public static final int PROFILE_TYPE_MANAGED = 1; 61 /** Respresents the profile type of the private profile. */ 62 public static final int PROFILE_TYPE_PRIVATE = 2; 63 64 @Retention(RetentionPolicy.SOURCE) 65 @IntDef(value = {PROFILE_TYPE_PRIMARY, PROFILE_TYPE_MANAGED, PROFILE_TYPE_PRIVATE}) 66 public @interface ProfileType { 67 // This array needs to cover all profile types. So whenever a new entry is added above then 68 // please remember to include it in this array as well. 69 int[] ALL_PROFILE_TYPES = 70 {PROFILE_TYPE_PRIMARY, PROFILE_TYPE_MANAGED, PROFILE_TYPE_PRIVATE}; 71 } 72 UserProfileGroup( @serIdInt int profileParentUserId, int[] managedProfilesUserIds, int[] managedRunningProfilesUserIds, @UserIdInt int privateProfileUserId, boolean privateProfileRunning)73 private UserProfileGroup( 74 @UserIdInt int profileParentUserId, 75 int[] managedProfilesUserIds, 76 int[] managedRunningProfilesUserIds, 77 @UserIdInt int privateProfileUserId, 78 boolean privateProfileRunning) { 79 mProfileParentUserId = profileParentUserId; 80 mManagedProfilesUserIds = managedProfilesUserIds; 81 mManagedRunningProfilesUserIds = managedRunningProfilesUserIds; 82 mPrivateProfileUserId = privateProfileUserId; 83 mPrivateProfileRunning = privateProfileRunning; 84 } 85 86 /** Returns all the alive {@link UserProfileGroup}s. */ getAllUserProfileGroups(Context context)87 public static List<UserProfileGroup> getAllUserProfileGroups(Context context) { 88 List<UserProfileGroup> userProfileGroups = new ArrayList<>(); 89 List<UserHandle> userHandles = UserUtils.getUserHandles(context); 90 for (int i = 0; i < userHandles.size(); i++) { 91 UserHandle userHandle = userHandles.get(i); 92 int userId = userHandle.getIdentifier(); 93 94 if (userProfileGroupsContain(userProfileGroups, userId)) { 95 continue; 96 } 97 98 UserProfileGroup userProfileGroup = UserProfileGroup.fromUser(context, userId); 99 if (!userProfileGroup.contains(userId)) { 100 continue; 101 } 102 103 userProfileGroups.add(userProfileGroup); 104 } 105 return userProfileGroups; 106 } 107 userProfileGroupsContain( List<UserProfileGroup> userProfileGroups, @UserIdInt int userId)108 private static boolean userProfileGroupsContain( 109 List<UserProfileGroup> userProfileGroups, @UserIdInt int userId) { 110 for (int i = 0; i < userProfileGroups.size(); i++) { 111 UserProfileGroup userProfileGroup = userProfileGroups.get(i); 112 113 if (userProfileGroup.contains(userId)) { 114 return true; 115 } 116 } 117 118 return false; 119 } 120 121 /** 122 * Returns the {@link UserProfileGroup} associated with the given {@code userId}. 123 * 124 * <p>The given {@code userId} could be related to the profile parent or any of its associated 125 * profile(s). 126 * 127 * <p>It is possible for the {@code userId} to not be contained within the returned {@link 128 * UserProfileGroup}. This can happen if the {@code userId} is a profile that is not managed or 129 * is disabled. 130 */ fromUser(Context context, @UserIdInt int userId)131 public static UserProfileGroup fromUser(Context context, @UserIdInt int userId) { 132 List<UserHandle> userProfiles = UserUtils.getUserProfiles(userId, context); 133 int profileParentUserId = UserUtils.getProfileParentIdOrSelf(userId, context); 134 135 int[] managedProfilesUserIds = new int[userProfiles.size()]; 136 int[] managedRunningProfilesUserIds = new int[userProfiles.size()]; 137 int managedProfilesUserIdsLen = 0; 138 int managedRunningProfilesUserIdsLen = 0; 139 140 int privateProfileUserId = UserHandleCompat.USER_NULL; 141 boolean privateProfileRunning = false; 142 143 for (int i = 0; i < userProfiles.size(); i++) { 144 UserHandle userProfileHandle = userProfiles.get(i); 145 int userProfileId = userProfileHandle.getIdentifier(); 146 147 if (UserUtils.isManagedProfile(userProfileId, context)) { 148 managedProfilesUserIds[managedProfilesUserIdsLen++] = userProfileId; 149 if (UserUtils.isProfileRunning(userProfileId, context)) { 150 managedRunningProfilesUserIds[managedRunningProfilesUserIdsLen++] = 151 userProfileId; 152 } 153 } else if (UserUtils.isPrivateProfile(userProfileId, context)) { 154 privateProfileUserId = userProfileId; 155 privateProfileRunning = UserUtils.isProfileRunning(userProfileId, context); 156 } 157 } 158 159 UserProfileGroup userProfileGroup = new UserProfileGroup( 160 profileParentUserId, 161 Arrays.copyOf(managedProfilesUserIds, managedProfilesUserIdsLen), 162 Arrays.copyOf(managedRunningProfilesUserIds, managedRunningProfilesUserIdsLen), 163 privateProfileUserId, 164 privateProfileRunning 165 ); 166 if (!userProfileGroup.contains(userId)) { 167 Log.i( 168 TAG, 169 "User id: " + userId + " does not belong to: " + userProfileGroup, 170 new Exception()); 171 } 172 return userProfileGroup; 173 } 174 175 /** Returns whether the given {@code userId} is supported by {@link UserProfileGroup}. */ isSupported(@serIdInt int userId, Context context)176 public static boolean isSupported(@UserIdInt int userId, Context context) { 177 if (!isProfile(userId, context)) { 178 return true; 179 } 180 return UserUtils.isManagedProfile(userId, context) 181 || UserUtils.isPrivateProfile(userId, context); 182 } 183 getUserManagerForUser(@serIdInt int userId, Context context)184 private static UserManager getUserManagerForUser(@UserIdInt int userId, Context context) { 185 Context userContext = UserUtils.getUserContext(userId, context); 186 return requireNonNull(userContext.getSystemService(UserManager.class)); 187 } 188 isProfile(@serIdInt int userId, Context context)189 private static boolean isProfile(@UserIdInt int userId, Context context) { 190 // This call requires the INTERACT_ACROSS_USERS permission. 191 final long callingId = Binder.clearCallingIdentity(); 192 try { 193 UserManager userManager = getUserManagerForUser(userId, context); 194 return userManager.isProfile(); 195 } finally { 196 Binder.restoreCallingIdentity(callingId); 197 } 198 } 199 200 /** Returns the profile parent user id of the {@link UserProfileGroup}. */ getProfileParentUserId()201 public int getProfileParentUserId() { 202 return mProfileParentUserId; 203 } 204 205 /** 206 * A convenience method to get all the profile ids of all the users of all profile types. So, in 207 * essence, this is equivalent to iterating through all the profile types using 208 * {@link ProfileType#ALL_PROFILE_TYPES} and getting all the users for each of the profile type 209 * using {@link #getProfilesOfType(int profileType)} 210 */ getAllProfilesUserIds()211 public int[] getAllProfilesUserIds() { 212 int[] allProfileIds = new int[getNumProfiles()]; 213 allProfileIds[0] = mProfileParentUserId; 214 System.arraycopy( 215 mManagedProfilesUserIds, 216 /* srcPos= */ 0, 217 allProfileIds, 218 /* destPos= */ 1, 219 mManagedProfilesUserIds.length); 220 221 if (mPrivateProfileUserId != UserHandleCompat.USER_NULL) { 222 allProfileIds[allProfileIds.length - 1] = mPrivateProfileUserId; 223 } 224 225 return allProfileIds; 226 } 227 228 /** 229 * A convenience method to get all the profile ids of all the users (that are currently running) 230 * of all profile types. So, in essence, this is equivalent to iterating through all the profile 231 * {types using {@link ProfileType#ALL_PROFILE_TYPES} and getting all the users for each of the 232 * profile type using {@link #getProfilesOfType(int profileType)} only if they are running. 233 */ getAllRunningProfilesUserIds()234 public int[] getAllRunningProfilesUserIds() { 235 int[] allRunningProfileIds = new int[getNumRunningProfiles()]; 236 allRunningProfileIds[0] = mProfileParentUserId; 237 System.arraycopy( 238 mManagedRunningProfilesUserIds, 239 /* srcPos= */ 0, 240 allRunningProfileIds, 241 /* destPos= */ 1, 242 mManagedRunningProfilesUserIds.length); 243 244 if (mPrivateProfileRunning) { 245 allRunningProfileIds[allRunningProfileIds.length - 1] = mPrivateProfileUserId; 246 } 247 248 return allRunningProfileIds; 249 } 250 251 /** 252 * Returns the profiles of the specified type. Returns an empty array if no profile of the 253 * specified type exists. 254 */ getProfilesOfType(@rofileType int profileType)255 public int[] getProfilesOfType(@ProfileType int profileType) { 256 switch (profileType) { 257 case PROFILE_TYPE_PRIMARY: 258 return new int[] {mProfileParentUserId}; 259 case PROFILE_TYPE_MANAGED: 260 return mManagedProfilesUserIds; 261 case PROFILE_TYPE_PRIVATE: 262 return mPrivateProfileUserId != UserHandleCompat.USER_NULL 263 ? new int[]{mPrivateProfileUserId} : new int[]{}; 264 default: 265 Log.w(TAG, "profiles requested for unexpected profile type " + profileType); 266 return new int[] {}; 267 } 268 } 269 270 /** 271 * Returns the running profiles of the specified type. Returns an empty array if no profile of 272 * the specified type exists. 273 */ getRunningProfilesOfType(@rofileType int profileType)274 public int[] getRunningProfilesOfType(@ProfileType int profileType) { 275 switch (profileType) { 276 case PROFILE_TYPE_PRIMARY: 277 return new int[] {mProfileParentUserId}; 278 case PROFILE_TYPE_MANAGED: 279 return mManagedRunningProfilesUserIds; 280 case PROFILE_TYPE_PRIVATE: 281 //TODO(b/286539356) add the new feature flag protection when available. 282 return mPrivateProfileRunning 283 ? new int[] {mPrivateProfileUserId} : new int[] {}; 284 default: 285 Log.w(TAG, "Unexpected profile type " + profileType); 286 return new int[] {}; 287 } 288 } 289 290 /** Returns the total number of running profiles in this user profile group */ getNumRunningProfiles()291 public int getNumRunningProfiles() { 292 return 1 293 + mManagedRunningProfilesUserIds.length 294 + (mPrivateProfileRunning ? 1 : 0); 295 } 296 297 /** Returns the total number of profiles in this user profile group */ getNumProfiles()298 private int getNumProfiles() { 299 return 1 300 + mManagedProfilesUserIds.length 301 + (mPrivateProfileUserId == UserHandleCompat.USER_NULL ? 0 : 1); 302 } 303 304 /** 305 * Returns the {@link ProfileType} for the provided {@code userId}. Note that the provided 306 * {@code userId} must be supported by the {@link UserProfileGroup} i.e. 307 * {@link #isSupported(int, Context)} should return true for {@code userId}. 308 */ getProfileTypeOfUser(@serIdInt int userId, Context context)309 public static @ProfileType int getProfileTypeOfUser(@UserIdInt int userId, Context context) { 310 if (UserUtils.isManagedProfile(userId, context)) { 311 return PROFILE_TYPE_MANAGED; 312 } 313 if (UserUtils.isPrivateProfile(userId, context)) { 314 return PROFILE_TYPE_PRIVATE; 315 } 316 return PROFILE_TYPE_PRIMARY; 317 } 318 319 /** 320 * Returns true iff the given userId is contained in this {@link UserProfileGroup} and it's 321 * running. 322 */ containsRunningUserId(@serIdInt int userId, @ProfileType int profileType)323 boolean containsRunningUserId(@UserIdInt int userId, @ProfileType int profileType) { 324 switch (profileType) { 325 case PROFILE_TYPE_PRIMARY: 326 return true; 327 case PROFILE_TYPE_MANAGED: 328 for (int i = 0; i < mManagedRunningProfilesUserIds.length; i++) { 329 if (mManagedRunningProfilesUserIds[i] == userId) { 330 return true; 331 } 332 } 333 return false; 334 case PROFILE_TYPE_PRIVATE: 335 return mPrivateProfileRunning; 336 default: 337 Log.w(TAG, "Unexpected profile type " + profileType); 338 return false; 339 } 340 } 341 342 /** Returns whether the {@link UserProfileGroup} contains the given {@code userId}. */ contains(@serIdInt int userId)343 public boolean contains(@UserIdInt int userId) { 344 if (userId == mProfileParentUserId) { 345 return true; 346 } 347 348 for (int i = 0; i < mManagedProfilesUserIds.length; i++) { 349 if (userId == mManagedProfilesUserIds[i]) { 350 return true; 351 } 352 } 353 354 return UserHandleCompat.USER_NULL != mPrivateProfileUserId 355 && userId == mPrivateProfileUserId; 356 } 357 358 @Override equals(Object o)359 public boolean equals(Object o) { 360 if (this == o) return true; 361 if (!(o instanceof UserProfileGroup)) return false; 362 UserProfileGroup that = (UserProfileGroup) o; 363 return mProfileParentUserId == that.mProfileParentUserId 364 && Arrays.equals(mManagedProfilesUserIds, that.mManagedProfilesUserIds) 365 && Arrays.equals( 366 mManagedRunningProfilesUserIds, that.mManagedRunningProfilesUserIds) 367 && mPrivateProfileUserId == that.mPrivateProfileUserId 368 && mPrivateProfileRunning == that.mPrivateProfileRunning; 369 } 370 371 @Override hashCode()372 public int hashCode() { 373 return Objects.hash( 374 mProfileParentUserId, 375 Arrays.hashCode(mManagedProfilesUserIds), 376 Arrays.hashCode(mManagedRunningProfilesUserIds), 377 mPrivateProfileUserId, 378 mPrivateProfileRunning); 379 } 380 381 @Override toString()382 public String toString() { 383 return "UserProfileGroup{" 384 + "mProfileParentUserId=" 385 + mProfileParentUserId 386 + ", mManagedProfilesUserIds=" 387 + Arrays.toString(mManagedProfilesUserIds) 388 + ", mManagedRunningProfilesUserIds=" 389 + Arrays.toString(mManagedRunningProfilesUserIds) 390 + ", mPrivateProfileUserId" 391 + mPrivateProfileUserId 392 + ", mPrivateProfileRunning" 393 + mPrivateProfileRunning 394 + '}'; 395 } 396 } 397