• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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