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