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 android.os.Build.VERSION_CODES.TIRAMISU; 20 21 import static java.util.Objects.requireNonNull; 22 23 import android.annotation.Nullable; 24 import android.annotation.UserIdInt; 25 import android.content.Context; 26 import android.content.pm.PackageManager; 27 import android.os.Binder; 28 import android.os.Process; 29 import android.os.UserHandle; 30 import android.os.UserManager; 31 import android.util.Log; 32 33 import androidx.annotation.RequiresApi; 34 35 import com.android.permission.util.UserUtils; 36 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 @RequiresApi(TIRAMISU) 49 public final class UserProfileGroup { 50 51 private static final String TAG = "UserProfileGroup"; 52 53 @UserIdInt private final int mProfileParentUserId; 54 private final int[] mManagedProfilesUserIds; 55 private final int[] mManagedRunningProfilesUserIds; 56 UserProfileGroup( @serIdInt int profileParentUserId, int[] managedProfilesUserIds, int[] managedRunningProfilesUserIds)57 private UserProfileGroup( 58 @UserIdInt int profileParentUserId, 59 int[] managedProfilesUserIds, 60 int[] managedRunningProfilesUserIds) { 61 mProfileParentUserId = profileParentUserId; 62 mManagedProfilesUserIds = managedProfilesUserIds; 63 mManagedRunningProfilesUserIds = managedRunningProfilesUserIds; 64 } 65 66 /** Returns all the alive {@link UserProfileGroup}s. */ getAllUserProfileGroups(Context context)67 public static List<UserProfileGroup> getAllUserProfileGroups(Context context) { 68 List<UserProfileGroup> userProfileGroups = new ArrayList<>(); 69 List<UserHandle> userHandles = UserUtils.getUserHandles(context); 70 for (int i = 0; i < userHandles.size(); i++) { 71 UserHandle userHandle = userHandles.get(i); 72 int userId = userHandle.getIdentifier(); 73 74 if (userProfileGroupsContain(userProfileGroups, userId)) { 75 continue; 76 } 77 78 UserProfileGroup userProfileGroup = UserProfileGroup.fromUser(context, userId); 79 if (!userProfileGroup.contains(userId)) { 80 continue; 81 } 82 83 userProfileGroups.add(userProfileGroup); 84 } 85 return userProfileGroups; 86 } 87 userProfileGroupsContain( List<UserProfileGroup> userProfileGroups, @UserIdInt int userId)88 private static boolean userProfileGroupsContain( 89 List<UserProfileGroup> userProfileGroups, @UserIdInt int userId) { 90 for (int i = 0; i < userProfileGroups.size(); i++) { 91 UserProfileGroup userProfileGroup = userProfileGroups.get(i); 92 93 if (userProfileGroup.contains(userId)) { 94 return true; 95 } 96 } 97 98 return false; 99 } 100 101 /** 102 * Returns the {@link UserProfileGroup} associated with the given {@code userId}. 103 * 104 * <p>The given {@code userId} could be related to the profile parent or any of its associated 105 * profile(s). 106 * 107 * <p>It is possible for the {@code userId} to not be contained within the returned {@link 108 * UserProfileGroup}. This can happen if the {@code userId} is a profile that is not managed or 109 * is disabled. 110 */ fromUser(Context context, @UserIdInt int userId)111 public static UserProfileGroup fromUser(Context context, @UserIdInt int userId) { 112 UserManager userManager = getUserManagerForUser(userId, context); 113 List<UserHandle> userProfiles = getEnabledUserProfiles(userManager); 114 UserHandle profileParent = getProfileParent(userManager, userId); 115 int profileParentUserId = userId; 116 if (profileParent != null) { 117 profileParentUserId = profileParent.getIdentifier(); 118 } 119 120 int[] managedProfilesUserIds = new int[userProfiles.size()]; 121 int[] managedRunningProfilesUserIds = new int[userProfiles.size()]; 122 int managedProfilesUserIdsLen = 0; 123 int managedRunningProfilesUserIdsLen = 0; 124 for (int i = 0; i < userProfiles.size(); i++) { 125 UserHandle userProfileHandle = userProfiles.get(i); 126 int userProfileId = userProfileHandle.getIdentifier(); 127 128 if (UserUtils.isManagedProfile(userProfileId, context)) { 129 managedProfilesUserIds[managedProfilesUserIdsLen++] = userProfileId; 130 if (UserUtils.isProfileRunning(userProfileId, context)) { 131 managedRunningProfilesUserIds[managedRunningProfilesUserIdsLen++] = 132 userProfileId; 133 } 134 } 135 } 136 137 UserProfileGroup userProfileGroup = 138 new UserProfileGroup( 139 profileParentUserId, 140 Arrays.copyOf(managedProfilesUserIds, managedProfilesUserIdsLen), 141 Arrays.copyOf( 142 managedRunningProfilesUserIds, managedRunningProfilesUserIdsLen)); 143 if (!userProfileGroup.contains(userId)) { 144 Log.w( 145 TAG, 146 "User id " + userId + " does not belong to " + userProfileGroup, 147 new Exception()); 148 } 149 return userProfileGroup; 150 } 151 152 /** Returns whether the given {@code userId} is supported by {@link UserProfileGroup}. */ isSupported(@serIdInt int userId, Context context)153 public static boolean isSupported(@UserIdInt int userId, Context context) { 154 if (!isProfile(userId, context)) { 155 return true; 156 } 157 return UserUtils.isManagedProfile(userId, context); 158 } 159 getUserManagerForUser(@serIdInt int userId, Context context)160 private static UserManager getUserManagerForUser(@UserIdInt int userId, Context context) { 161 Context userContext = getUserContext(context, UserHandle.of(userId)); 162 return requireNonNull(userContext.getSystemService(UserManager.class)); 163 } 164 getUserContext(Context context, UserHandle userHandle)165 private static Context getUserContext(Context context, UserHandle userHandle) { 166 if (Process.myUserHandle().equals(userHandle)) { 167 return context; 168 } else { 169 try { 170 return context.createPackageContextAsUser(context.getPackageName(), 0, userHandle); 171 } catch (PackageManager.NameNotFoundException doesNotHappen) { 172 throw new IllegalStateException(doesNotHappen); 173 } 174 } 175 } 176 isProfile(@serIdInt int userId, Context context)177 private static boolean isProfile(@UserIdInt int userId, Context context) { 178 // This call requires the INTERACT_ACROSS_USERS permission. 179 final long callingId = Binder.clearCallingIdentity(); 180 try { 181 UserManager userManager = getUserManagerForUser(userId, context); 182 return userManager.isProfile(); 183 } finally { 184 Binder.restoreCallingIdentity(callingId); 185 } 186 } 187 getEnabledUserProfiles(UserManager userManager)188 private static List<UserHandle> getEnabledUserProfiles(UserManager userManager) { 189 // This call requires the QUERY_USERS permission. 190 final long callingId = Binder.clearCallingIdentity(); 191 try { 192 return userManager.getUserProfiles(); 193 } finally { 194 Binder.restoreCallingIdentity(callingId); 195 } 196 } 197 198 @Nullable getProfileParent(UserManager userManager, @UserIdInt int userId)199 private static UserHandle getProfileParent(UserManager userManager, @UserIdInt int userId) { 200 // This call requires the INTERACT_ACROSS_USERS permission. 201 final long callingId = Binder.clearCallingIdentity(); 202 try { 203 return userManager.getProfileParent(UserHandle.of(userId)); 204 } finally { 205 Binder.restoreCallingIdentity(callingId); 206 } 207 } 208 209 /** Returns the profile parent user id of the {@link UserProfileGroup}. */ getProfileParentUserId()210 public int getProfileParentUserId() { 211 return mProfileParentUserId; 212 } 213 214 /** Returns the managed profile user ids of the {@link UserProfileGroup}. */ getManagedProfilesUserIds()215 public int[] getManagedProfilesUserIds() { 216 return mManagedProfilesUserIds; 217 } 218 219 /** Returns the running managed profile user ids of the {@link UserProfileGroup}. */ getManagedRunningProfilesUserIds()220 public int[] getManagedRunningProfilesUserIds() { 221 return mManagedRunningProfilesUserIds; 222 } 223 224 /** 225 * Convenience method that combines the results of {@link 226 * UserProfileGroup#getProfileParentUserId()} and {@link 227 * UserProfileGroup#getManagedRunningProfilesUserIds()}. 228 */ getProfileParentAndManagedRunningProfilesUserIds()229 public int[] getProfileParentAndManagedRunningProfilesUserIds() { 230 int[] profileParentAndManagedRunningProfilesUserIds = 231 new int[mManagedRunningProfilesUserIds.length + 1]; 232 profileParentAndManagedRunningProfilesUserIds[0] = mProfileParentUserId; 233 System.arraycopy( 234 mManagedRunningProfilesUserIds, 235 0, 236 profileParentAndManagedRunningProfilesUserIds, 237 1, 238 mManagedRunningProfilesUserIds.length); 239 return profileParentAndManagedRunningProfilesUserIds; 240 } 241 242 /** Returns whether the {@link UserProfileGroup} contains the given {@code userId}. */ contains(@serIdInt int userId)243 public boolean contains(@UserIdInt int userId) { 244 if (userId == mProfileParentUserId) { 245 return true; 246 } 247 248 for (int i = 0; i < mManagedProfilesUserIds.length; i++) { 249 if (userId == mManagedProfilesUserIds[i]) { 250 return true; 251 } 252 } 253 254 return false; 255 } 256 257 /** Returns whether the given {@code userId} is associated with a running managed profile. */ isManagedUserRunning(@serIdInt int userId)258 boolean isManagedUserRunning(@UserIdInt int userId) { 259 for (int i = 0; i < mManagedRunningProfilesUserIds.length; i++) { 260 if (userId == mManagedRunningProfilesUserIds[i]) { 261 return true; 262 } 263 } 264 return false; 265 } 266 267 @Override equals(Object o)268 public boolean equals(Object o) { 269 if (this == o) return true; 270 if (!(o instanceof UserProfileGroup)) return false; 271 UserProfileGroup that = (UserProfileGroup) o; 272 return mProfileParentUserId == that.mProfileParentUserId 273 && Arrays.equals(mManagedProfilesUserIds, that.mManagedProfilesUserIds) 274 && Arrays.equals( 275 mManagedRunningProfilesUserIds, that.mManagedRunningProfilesUserIds); 276 } 277 278 @Override hashCode()279 public int hashCode() { 280 return Objects.hash( 281 mProfileParentUserId, 282 Arrays.hashCode(mManagedProfilesUserIds), 283 Arrays.hashCode(mManagedRunningProfilesUserIds)); 284 } 285 286 @Override toString()287 public String toString() { 288 return "UserProfileGroup{" 289 + "mProfileParentUserId=" 290 + mProfileParentUserId 291 + ", mManagedProfilesUserIds=" 292 + Arrays.toString(mManagedProfilesUserIds) 293 + ", mManagedRunningProfilesUserIds=" 294 + Arrays.toString(mManagedRunningProfilesUserIds) 295 + '}'; 296 } 297 } 298