• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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.permission.util;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.annotation.UserIdInt;
22 import android.app.admin.DevicePolicyManager;
23 import android.content.Context;
24 import android.os.Binder;
25 import android.os.Process;
26 import android.os.UserHandle;
27 import android.os.UserManager;
28 import android.permission.internal.compat.UserHandleCompat;
29 
30 import com.android.internal.util.Preconditions;
31 import com.android.modules.utils.build.SdkLevel;
32 import com.android.permission.flags.Flags;
33 
34 import java.util.ArrayList;
35 import java.util.List;
36 
37 /** Utility class to deal with Android users. */
38 public final class UserUtils {
39 
UserUtils()40     private UserUtils() {}
41 
42     /** Enforces cross user permission for the calling UID and the given {@code userId}. */
enforceCrossUserPermission( @serIdInt int userId, boolean allowAll, boolean enforceForProfileGroup, @NonNull String message, @NonNull Context context)43     public static void enforceCrossUserPermission(
44             @UserIdInt int userId,
45             boolean allowAll,
46             boolean enforceForProfileGroup,
47             @NonNull String message,
48             @NonNull Context context) {
49         final int callingUid = Binder.getCallingUid();
50         final int callingUserId = UserHandleCompat.getUserId(callingUid);
51         if (userId == callingUserId && !enforceForProfileGroup) {
52             return;
53         }
54         Preconditions.checkArgument(
55                 userId >= UserHandleCompat.USER_SYSTEM
56                         || (allowAll && userId == UserHandleCompat.USER_ALL),
57                 "Invalid user " + userId);
58         context.enforceCallingOrSelfPermission(
59                 android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, message);
60         if (callingUid != Process.SHELL_UID || userId == UserHandleCompat.USER_ALL) {
61             return;
62         }
63 
64         if (enforceForProfileGroup) {
65             DevicePolicyManager devicePolicyManager =
66                     context.getSystemService(DevicePolicyManager.class);
67             if (!devicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile()) {
68                 // For profileGroup exclusive roles users on BYOD are free to choose personal or
69                 // work profile app regardless of DISALLOW_DEBUGGING_FEATURES
70                 return;
71             }
72 
73             List<UserHandle> profiles = getUserProfiles(userId, context, true);
74             final int profilesSize = profiles.size();
75             for (int i = 0; i < profilesSize; i++) {
76                 int profileId = profiles.get(i).getIdentifier();
77                 if (profileId == callingUserId) {
78                     continue;
79                 }
80                 enforceShellRestriction(profileId, context);
81             }
82         } else {
83             enforceShellRestriction(userId, context);
84         }
85     }
86 
enforceShellRestriction(int userId, @NonNull Context context)87     private static void enforceShellRestriction(int userId, @NonNull Context context) {
88         UserManager userManager = context.getSystemService(UserManager.class);
89         if (userManager.hasUserRestrictionForUser(
90                 UserManager.DISALLOW_DEBUGGING_FEATURES, UserHandle.of(userId))) {
91             throw new SecurityException(
92                     "Shell does not have permission to access user " + userId);
93         }
94     }
95 
96     /** Returns whether a given {@code userId} corresponds to an existing user. */
isUserExistent(@serIdInt int userId, @NonNull Context context)97     public static boolean isUserExistent(@UserIdInt int userId, @NonNull Context context) {
98         return getUserHandles(context).contains(UserHandle.of(userId));
99     }
100 
101     /** Returns all the alive users on the device. */
102     @NonNull
getUserHandles(@onNull Context context)103     public static List<UserHandle> getUserHandles(@NonNull Context context) {
104         UserManager userManager = context.getSystemService(UserManager.class);
105         // This call requires the MANAGE_USERS permission.
106         final long identity = Binder.clearCallingIdentity();
107         try {
108             return userManager.getUserHandles(true);
109         } finally {
110             Binder.restoreCallingIdentity(identity);
111         }
112     }
113 
114     /** Returns all the enabled user profiles on the device for a specified user. */
115     @NonNull
getUserProfiles(@serIdInt int userId, @NonNull Context context)116     public static List<UserHandle> getUserProfiles(@UserIdInt int userId,
117             @NonNull Context context) {
118         return getUserProfiles(userId, context, false);
119     }
120 
121     /**
122      * Returns all the enabled user profiles on the device for a specified user
123      *
124      * @param userId the user id to check
125      * @param context the {@link Context}
126      * @param excludePrivate {@code true} to exclude private profiles from returned list of users
127      */
128     @NonNull
getUserProfiles(@serIdInt int userId, @NonNull Context context, boolean excludePrivate)129     public static List<UserHandle> getUserProfiles(@UserIdInt int userId, @NonNull Context context,
130             boolean excludePrivate) {
131         // This call requires the QUERY_USERS permission.
132         final long identity = Binder.clearCallingIdentity();
133         try {
134             Context userContext = getUserContext(userId, context);
135             UserManager userUserManager = userContext.getSystemService(UserManager.class);
136             List<UserHandle> profiles = userUserManager.getUserProfiles();
137             if (!excludePrivate) {
138                 return profiles;
139             }
140             List<UserHandle> filteredProfiles = new ArrayList<>();
141             final int profilesSize = profiles.size();
142             for (int i = 0; i < profilesSize; i++) {
143                 UserHandle user = profiles.get(i);
144                 if (!isPrivateProfile(user.getIdentifier(), userContext)) {
145                     filteredProfiles.add(user);
146                 }
147             }
148             return filteredProfiles;
149         } finally {
150             Binder.restoreCallingIdentity(identity);
151         }
152     }
153 
154     /**
155      * Returns the parent of a given user, or userId if it has no parent (e.g. it is the primary
156      * profile)
157      */
158     @UserIdInt
getProfileParentIdOrSelf(@serIdInt int userId, @NonNull Context context)159     public static int getProfileParentIdOrSelf(@UserIdInt int userId, @NonNull Context context) {
160         UserHandle profileParent = getProfileParent(userId, context);
161         // If profile parent userhandle is null, then original user id is the parent
162         return profileParent != null ? profileParent.getIdentifier() : userId;
163     }
164 
165     /** Returns the parent of a given user. */
166     @Nullable
getProfileParent(@serIdInt int userId, @NonNull Context context)167     private static UserHandle getProfileParent(@UserIdInt int userId, @NonNull Context context) {
168         Context userContext = getUserContext(userId, context);
169         UserManager userUserManager = userContext.getSystemService(UserManager.class);
170         // This call requires the INTERACT_ACROSS_USERS permission.
171         final long identity = Binder.clearCallingIdentity();
172         try {
173             return userUserManager.getProfileParent(UserHandle.of(userId));
174         } finally {
175             Binder.restoreCallingIdentity(identity);
176         }
177     }
178 
179     /** Returns whether a given {@code userId} corresponds to a managed profile. */
isManagedProfile(@serIdInt int userId, @NonNull Context context)180     public static boolean isManagedProfile(@UserIdInt int userId, @NonNull Context context) {
181         UserManager userManager = context.getSystemService(UserManager.class);
182         // This call requires the QUERY_USERS permission.
183         final long identity = Binder.clearCallingIdentity();
184         try {
185             return userManager.isManagedProfile(userId);
186         } finally {
187             Binder.restoreCallingIdentity(identity);
188         }
189     }
190 
191     /**
192      * Returns whether the given {@code userId} is a private profile. Note that private profiles are
193      * allowed from Android V+ only, so this method will return false on Sdk levels below that.
194      */
isPrivateProfile(@serIdInt int userId, @NonNull Context context)195     public static boolean isPrivateProfile(@UserIdInt int userId, @NonNull Context context) {
196         if (!isPrivateProfileSupported()) {
197             return false;
198         }
199         // It's needed to clear the calling identity because we are going to query the UserManager
200         // for isPrivateProfile() and Context for createContextAsUser, which requires one of the
201         // following permissions:
202         // MANAGE_USERS, QUERY_USERS, or INTERACT_ACROSS_USERS.
203         final long identity = Binder.clearCallingIdentity();
204         try {
205             Context userContext = getUserContext(userId, context);
206             UserManager userUserManager = userContext.getSystemService(UserManager.class);
207             return userUserManager != null && userUserManager.isPrivateProfile();
208         } finally {
209             Binder.restoreCallingIdentity(identity);
210         }
211     }
212 
213 
214     /**
215      * Returns whether private profile's allowed to exist.  This can be true iff the SdkLevel is at
216      * least V AND the permission module's private profile feature flag is enabled.
217      */
isPrivateProfileSupported()218     public static boolean isPrivateProfileSupported() {
219         //TODO(b/286539356) add the os feature flag protection when available.
220         return SdkLevel.isAtLeastV() && Flags.privateProfileSupported();
221     }
222 
223     /**
224      * Returns whether a given {@code userId} corresponds to a running managed profile, i.e. the
225      * user is running and the quiet mode is not enabled.
226      */
isProfileRunning(@serIdInt int userId, @NonNull Context context)227     public static boolean isProfileRunning(@UserIdInt int userId, @NonNull Context context) {
228         UserManager userManager = context.getSystemService(UserManager.class);
229         // This call requires the QUERY_USERS permission
230         final long identity = Binder.clearCallingIdentity();
231         try {
232             return userManager.isUserRunning(UserHandle.of(userId))
233                     && !userManager.isQuietModeEnabled(UserHandle.of(userId));
234         } finally {
235             Binder.restoreCallingIdentity(identity);
236         }
237     }
238 
239     @NonNull
getUserContext(@serIdInt int userId, @NonNull Context context)240     public static Context getUserContext(@UserIdInt int userId, @NonNull Context context) {
241         if (SdkLevel.isAtLeastS() && context.getUser().getIdentifier() == userId) {
242             return context;
243         } else {
244             return context.createContextAsUser(UserHandle.of(userId), 0);
245         }
246     }
247 }
248