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