diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 2d28425bdc36b..21004bfeeb195 100755 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -8681,15 +8681,15 @@ package android.os { method @RequiresPermission(allOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public int getUserSwitchability(); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean hasRestrictedProfiles(); method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean hasUserRestrictionForUser(@NonNull String, @NonNull android.os.UserHandle); - method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isAdminUser(); + method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean isAdminUser(); method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean isCloneProfile(); method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean isGuestUser(); method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean isManagedProfile(int); method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean isMediaSharedWithParent(); method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean isPrimaryUser(); method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean isProfile(); - method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isRestrictedProfile(); - method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean isRestrictedProfile(@NonNull android.os.UserHandle); + method public boolean isRestrictedProfile(); + method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}, conditional=true) public boolean isRestrictedProfile(@NonNull android.os.UserHandle); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isSameProfileGroup(@NonNull android.os.UserHandle, @NonNull android.os.UserHandle); method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED}) public boolean isUserNameSet(); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isUserOfType(@NonNull String); diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl index 3bee4b73dc226..b83970625ee4c 100644 --- a/core/java/android/os/IUserManager.aidl +++ b/core/java/android/os/IUserManager.aidl @@ -69,7 +69,7 @@ interface IUserManager { String getUserAccount(int userId); void setUserAccount(int userId, String accountName); long getUserCreationTime(int userId); - boolean isRestricted(); + boolean isRestricted(int userId); boolean canHaveRestrictedProfile(int userId); int getUserSerialNumber(int userId); int getUserHandle(int userSerialNumber); @@ -93,10 +93,10 @@ interface IUserManager { boolean isQuietModeEnabled(int userId); void setSeedAccountData(int userId, in String accountName, in String accountType, in PersistableBundle accountOptions, boolean persist); - String getSeedAccountName(); - String getSeedAccountType(); - PersistableBundle getSeedAccountOptions(); - void clearSeedAccountData(); + String getSeedAccountName(int userId); + String getSeedAccountType(int userId); + PersistableBundle getSeedAccountOptions(int userId); + void clearSeedAccountData(int userId); boolean someUserHasSeedAccount(in String accountName, in String accountType); boolean isProfile(int userId); boolean isManagedProfile(int userId); @@ -118,7 +118,7 @@ interface IUserManager { boolean isUserRunning(int userId); boolean isUserForeground(int userId); boolean isUserNameSet(int userId); - boolean hasRestrictedProfiles(); + boolean hasRestrictedProfiles(int userId); boolean requestQuietModeEnabled(String callingPackage, boolean enableQuietMode, int userId, in IntentSender target, int flags); String getUserName(); long getUserStartRealtime(); diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 09b7aaa9545c2..da0937531c03c 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -37,6 +37,9 @@ import android.app.Activity; import android.app.ActivityManager; import android.app.PropertyInvalidatedCache; import android.app.admin.DevicePolicyManager; +import android.app.compat.CompatChanges; +import android.compat.annotation.ChangeId; +import android.compat.annotation.EnabledSince; import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; @@ -55,6 +58,7 @@ import android.provider.Settings; import android.telephony.TelephonyManager; import android.util.AndroidException; import android.util.ArraySet; +import android.util.Log; import android.view.WindowManager.LayoutParams; import com.android.internal.R; @@ -1719,6 +1723,61 @@ public class UserManager { } } + /** + * For apps targeting {@link Build.VERSION_CODES#TIRAMISU} and above, any UserManager API marked + * as {@link android.annotation.UserHandleAware @UserHandleAware} will use the context user + * (rather than the calling user). + * For apps targeting an SDK version below this, the behaviour + * depends on the particular method and when it was first introduced: + * + * + * Note that when an API marked with + * {@link android.annotation.UserHandleAware#enabledSinceTargetSdkVersion} is run + * on a device whose OS predates that version, the calling user will be used, since on such a + * device, the API is not {@literal @}UserHandleAware yet. + * + * @hide + */ + @ChangeId + @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU) + public static final long ALWAYS_USE_CONTEXT_USER = 183155436L; + + /** + * Returns the context user or the calling user, depending on the target SDK. + * New APIs do not require such gating and therefore should always use mUserId instead. + * @see #ALWAYS_USE_CONTEXT_USER + */ + private @UserIdInt int getContextUserIfAppropriate() { + if (CompatChanges.isChangeEnabled(ALWAYS_USE_CONTEXT_USER)) { + return mUserId; + } else { + final int callingUser = UserHandle.myUserId(); + if (callingUser != mUserId) { + Log.w(TAG, "Using the calling user " + callingUser + + ", rather than the specified context user " + mUserId + + ", because API is only UserHandleAware on higher targetSdkVersions.", + new Throwable()); + } + return callingUser; + } + } + /** @hide */ @UnsupportedAppUsage public static UserManager get(Context context) { @@ -1784,7 +1843,12 @@ public class UserManager { * @hide */ @Deprecated + @RequiresPermission(allOf = { + Manifest.permission.READ_PHONE_STATE, + Manifest.permission.MANAGE_USERS}, // Can be INTERACT_ACROSS_USERS instead. + conditional = true) @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UserHandleAware() public boolean canSwitchUsers() { boolean allowUserSwitchingWhenSystemUserLocked = Settings.Global.getInt( mContext.getContentResolver(), @@ -1795,14 +1859,13 @@ public class UserManager { if (telephonyManager != null) { inCall = telephonyManager.getCallState() != TelephonyManager.CALL_STATE_IDLE; } - boolean isUserSwitchDisallowed = hasUserRestriction(DISALLOW_USER_SWITCH); + boolean isUserSwitchDisallowed = hasUserRestrictionForUser(DISALLOW_USER_SWITCH, mUserId); return (allowUserSwitchingWhenSystemUserLocked || isSystemUserUnlocked) && !inCall && !isUserSwitchDisallowed; } /** - * Returns whether switching users is currently allowed for the user this process is running - * under. + * Returns whether switching users is currently allowed for the context user. *

* Switching users is not allowed in the following cases: *

  • the user is in a phone call
  • @@ -1816,8 +1879,9 @@ public class UserManager { @RequiresPermission(allOf = {Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true) + @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU) public @UserSwitchabilityResult int getUserSwitchability() { - return getUserSwitchability(Process.myUserHandle()); + return getUserSwitchability(UserHandle.of(getContextUserIfAppropriate())); } /** @@ -1842,7 +1906,7 @@ public class UserManager { if (tm.getCallState() != TelephonyManager.CALL_STATE_IDLE) { flags |= SWITCHABILITY_STATUS_USER_IN_CALL; } - if (hasUserRestriction(DISALLOW_USER_SWITCH, userHandle)) { + if (hasUserRestrictionForUser(DISALLOW_USER_SWITCH, userHandle)) { flags |= SWITCHABILITY_STATUS_USER_SWITCH_DISALLOWED; } @@ -1862,14 +1926,16 @@ public class UserManager { } /** - * Returns the user handle for the user that this process is running under. + * Returns the userId for the context user. * - * @return the user handle of this process. + * @return the userId of the context user. * @hide */ @UnsupportedAppUsage + @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU) + // *** Do NOT use this in UserManager. Instead always use mUserId. *** public @UserIdInt int getUserHandle() { - return UserHandle.myUserId(); + return getContextUserIfAppropriate(); } /** @@ -1877,9 +1943,9 @@ public class UserManager { * @hide */ @TestApi - @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS, - android.Manifest.permission.CREATE_USERS - }) + @RequiresPermission(anyOf = { + android.Manifest.permission.MANAGE_USERS, + android.Manifest.permission.CREATE_USERS}) @UserHandleAware public @NonNull String getUserType() { UserInfo userInfo = getUserInfo(mUserId); @@ -1919,9 +1985,10 @@ public class UserManager { @SystemApi @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS, Manifest.permission.GET_ACCOUNTS_PRIVILEGED}) + @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU) public boolean isUserNameSet() { try { - return mService.isUserNameSet(getUserHandle()); + return mService.isUserNameSet(getContextUserIfAppropriate()); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } @@ -1939,52 +2006,60 @@ public class UserManager { * * @return Returns whether the user making this call is a goat. */ + @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU) public boolean isUserAGoat() { if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.R) { return false; } + // Caution: This is NOT @UserHandleAware (because mContext is getApplicationContext and + // holds a different userId), but for R+ it returns false, so it doesn't matter anyway. return mContext.getPackageManager() .isPackageAvailable("com.coffeestainstudios.goatsimulator"); } /** - * Used to check if this process is running under the primary user. The primary user + * Used to check if the context user is the primary user. The primary user * is the first human user on a device. This is not supported in headless system user mode. * - * @return whether this process is running under the primary user. + * @return whether the context user is the primary user. * @hide */ @SystemApi @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS, Manifest.permission.CREATE_USERS}) + @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU) public boolean isPrimaryUser() { - UserInfo user = getUserInfo(UserHandle.myUserId()); + final UserInfo user = getUserInfo(getContextUserIfAppropriate()); return user != null && user.isPrimary(); } /** - * Used to check if this process is running under the system user. The system user + * Used to check if the context user is the system user. The system user * is the initial user that is implicitly created on first boot and hosts most of the * system services. * - * @return whether this process is running under the system user. + * @return whether the context user is the system user. */ + @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU) public boolean isSystemUser() { - return UserHandle.myUserId() == UserHandle.USER_SYSTEM; + return getContextUserIfAppropriate() == UserHandle.USER_SYSTEM; } /** - * Used to check if this process is running as an admin user. An admin user is allowed to + * Used to check if the context user is an admin user. An admin user is allowed to * modify or configure certain settings that aren't available to non-admin users, * create and delete additional users, etc. There can be more than one admin users. * - * @return whether this process is running under an admin user. + * @return whether the context user is an admin user. * @hide */ @SystemApi - @RequiresPermission(android.Manifest.permission.MANAGE_USERS) + @RequiresPermission(anyOf = { + Manifest.permission.MANAGE_USERS, + Manifest.permission.CREATE_USERS}) + @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU) public boolean isAdminUser() { - return isUserAdmin(UserHandle.myUserId()); + return isUserAdmin(getContextUserIfAppropriate()); } /** @@ -2099,22 +2174,33 @@ public class UserManager { */ @UnsupportedAppUsage @Deprecated + @UserHandleAware( + enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU, + requiresAnyOfPermissionsIfNotCaller = { + android.Manifest.permission.MANAGE_USERS, + android.Manifest.permission.CREATE_USERS} + ) public boolean isLinkedUser() { return isRestrictedProfile(); } /** - * Used to check if this process is running under a restricted profile. Restricted profiles + * Used to check if the context user is a restricted profile. Restricted profiles * may have a reduced number of available apps, app restrictions, and account restrictions. * - * @return whether this process is running under a restricted profile. + * @return whether the context user is a restricted profile. * @hide */ @SystemApi - @RequiresPermission(android.Manifest.permission.MANAGE_USERS) + @UserHandleAware( + enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU, + requiresAnyOfPermissionsIfNotCaller = { + android.Manifest.permission.MANAGE_USERS, + android.Manifest.permission.CREATE_USERS} + ) public boolean isRestrictedProfile() { try { - return mService.isRestricted(); + return mService.isRestricted(getContextUserIfAppropriate()); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } @@ -2129,11 +2215,13 @@ public class UserManager { * @hide */ @SystemApi - @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS, - Manifest.permission.CREATE_USERS}) + @RequiresPermission(anyOf = { + Manifest.permission.MANAGE_USERS, + Manifest.permission.CREATE_USERS}, + conditional = true) public boolean isRestrictedProfile(@NonNull UserHandle user) { try { - return mService.getUserInfo(user.getIdentifier()).isRestricted(); + return mService.isRestricted(user.getIdentifier()); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } @@ -2156,15 +2244,16 @@ public class UserManager { } /** - * Returns whether the calling user has at least one restricted profile associated with it. - * @return + * Returns whether the context user has at least one restricted profile associated with it. + * @return whether the user has a restricted profile associated with it * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.MANAGE_USERS) + @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU) public boolean hasRestrictedProfiles() { try { - return mService.hasRestrictedProfiles(); + return mService.hasRestrictedProfiles(getContextUserIfAppropriate()); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } @@ -2203,29 +2292,34 @@ public class UserManager { } /** - * Used to check if this process is running under a guest user. A guest user may be transient. + * Used to check if the context user is a guest user. A guest user may be transient. * - * @return whether this process is running under a guest user. + * @return whether the context user is a guest user. * @hide */ @SystemApi @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS, Manifest.permission.CREATE_USERS}) + @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU) public boolean isGuestUser() { - UserInfo user = getUserInfo(UserHandle.myUserId()); + UserInfo user = getUserInfo(getContextUserIfAppropriate()); return user != null && user.isGuest(); } /** - * Checks if the calling app is running in a demo user. When running in a demo user, + * Checks if the context user is a demo user. When running in a demo user, * apps can be more helpful to the user, or explain their features in more detail. * - * @return whether the caller is a demo user. + * @return whether the context user is a demo user. */ + @UserHandleAware( + enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU, + requiresPermissionIfNotCaller = android.Manifest.permission.MANAGE_USERS + ) public boolean isDemoUser() { try { - return mService.isDemoUser(UserHandle.myUserId()); + return mService.isDemoUser(getContextUserIfAppropriate()); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } @@ -2250,14 +2344,14 @@ public class UserManager { } private boolean isProfile(@UserIdInt int userId) { - if (userId == UserHandle.myUserId()) { + if (userId == mUserId) { // No need for synchronization. Once it becomes non-null, it'll be non-null forever. // Worst case we might end up calling the AIDL method multiple times but that's fine. if (mIsProfileCached != null) { return mIsProfileCached; } try { - mIsProfileCached = mService.isProfile(userId); + mIsProfileCached = mService.isProfile(mUserId); return mIsProfileCached; } catch (RemoteException re) { throw re.rethrowFromSystemServer(); @@ -2272,22 +2366,18 @@ public class UserManager { } /** - * Checks if the calling app is running in a managed profile. + * Checks if the context user is a managed profile. * - * @return whether the caller is in a managed profile. + * @return whether the context user is a managed profile. */ + @UserHandleAware( + enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU, + requiresAnyOfPermissionsIfNotCallerProfileGroup = { + android.Manifest.permission.MANAGE_USERS, + android.Manifest.permission.INTERACT_ACROSS_USERS} + ) public boolean isManagedProfile() { - // No need for synchronization. Once it becomes non-null, it'll be non-null forever. - // Worst case we might end up calling the AIDL method multiple times but that's fine. - if (mIsManagedProfileCached != null) { - return mIsManagedProfileCached; - } - try { - mIsManagedProfileCached = mService.isManagedProfile(UserHandle.myUserId()); - return mIsManagedProfileCached; - } catch (RemoteException re) { - throw re.rethrowFromSystemServer(); - } + return isManagedProfile(getContextUserIfAppropriate()); } /** @@ -2303,13 +2393,24 @@ public class UserManager { @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS, Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true) public boolean isManagedProfile(@UserIdInt int userId) { - if (userId == UserHandle.myUserId()) { - return isManagedProfile(); - } - try { - return mService.isManagedProfile(userId); - } catch (RemoteException re) { - throw re.rethrowFromSystemServer(); + if (userId == mUserId) { + // No need for synchronization. Once it becomes non-null, it'll be non-null forever. + // Worst case we might end up calling the AIDL method multiple times but that's fine. + if (mIsManagedProfileCached != null) { + return mIsManagedProfileCached; + } + try { + mIsManagedProfileCached = mService.isManagedProfile(mUserId); + return mIsManagedProfileCached; + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } else { + try { + return mService.isManagedProfile(userId); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } } } @@ -2339,15 +2440,16 @@ public class UserManager { } /** - * Checks if the calling app is running as an ephemeral user. + * Checks if the context user is an ephemeral user. * - * @return whether the caller is an ephemeral user. + * @return whether the context user is an ephemeral user. * @hide */ @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS, Manifest.permission.CREATE_USERS}) + @UserHandleAware() public boolean isEphemeralUser() { - return isUserEphemeral(UserHandle.myUserId()); + return isUserEphemeral(mUserId); } /** @@ -2436,7 +2538,7 @@ public class UserManager { } /** - * Return whether the calling user is running in an "unlocked" state. + * Return whether the context user is running in an "unlocked" state. *

    * On devices with direct boot, a user is unlocked only after they've * entered their credentials (such as a lock pattern or PIN). On devices @@ -2449,8 +2551,14 @@ public class UserManager { * @see Intent#ACTION_USER_UNLOCKED * @see Context#createDeviceProtectedStorageContext() */ + @UserHandleAware( + enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU, + requiresAnyOfPermissionsIfNotCallerProfileGroup = { + android.Manifest.permission.MANAGE_USERS, + android.Manifest.permission.INTERACT_ACROSS_USERS} + ) public boolean isUserUnlocked() { - return isUserUnlocked(Process.myUserHandle()); + return isUserUnlocked(getContextUserIfAppropriate()); } /** @@ -2573,7 +2681,14 @@ public class UserManager { * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + // NOT @UserHandleAware public long getUserStartRealtime() { + if (getContextUserIfAppropriate() != UserHandle.myUserId()) { + // Note: If we want to support this in the future, also annotate with + // @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU) + throw new IllegalArgumentException("Calling from a context differing from the calling " + + "user is not currently supported."); + } try { return mService.getUserStartRealtime(); } catch (RemoteException re) { @@ -2582,13 +2697,20 @@ public class UserManager { } /** - * Return the time when the calling user was unlocked elapsed milliseconds since boot, + * Return the time when the context user was unlocked elapsed milliseconds since boot, * or 0 if not unlocked. * * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + // NOT @UserHandleAware public long getUserUnlockRealtime() { + if (getContextUserIfAppropriate() != UserHandle.myUserId()) { + // Note: If we want to support this in the future, also annotate with + // @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU) + throw new IllegalArgumentException("Calling from a context differing from the calling " + + "user is not currently supported."); + } try { return mService.getUserUnlockRealtime(); } catch (RemoteException re) { @@ -2657,11 +2779,21 @@ public class UserManager { } /** - * Returns the user-wide restrictions imposed on this user. + * Returns the user-wide restrictions imposed on the context user. * @return a Bundle containing all the restrictions. */ + @UserHandleAware( + enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU, + requiresAnyOfPermissionsIfNotCallerProfileGroup = { + android.Manifest.permission.MANAGE_USERS, + android.Manifest.permission.INTERACT_ACROSS_USERS} + ) public Bundle getUserRestrictions() { - return getUserRestrictions(Process.myUserHandle()); + try { + return mService.getUserRestrictions(getContextUserIfAppropriate()); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } } /** @@ -2727,7 +2859,7 @@ public class UserManager { } /** - * Sets the value of a specific restriction. + * Sets the value of a specific restriction on the context user. * Requires the MANAGE_USERS permission. * @param key the key of the restriction * @param value the value for the restriction @@ -2738,8 +2870,13 @@ public class UserManager { */ @Deprecated @RequiresPermission(Manifest.permission.MANAGE_USERS) + @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU) public void setUserRestriction(String key, boolean value) { - setUserRestriction(key, value, Process.myUserHandle()); + try { + mService.setUserRestriction(key, value, getContextUserIfAppropriate()); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } } /** @@ -2764,14 +2901,20 @@ public class UserManager { } /** - * Returns whether the current user has been disallowed from performing certain actions + * Returns whether the context user has been disallowed from performing certain actions * or setting certain settings. * * @param restrictionKey The string key representing the restriction. - * @return {@code true} if the current user has the given restriction, {@code false} otherwise. + * @return {@code true} if the context user has the given restriction, {@code false} otherwise. */ + @UserHandleAware( + enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU, + requiresAnyOfPermissionsIfNotCallerProfileGroup = { + android.Manifest.permission.MANAGE_USERS, + android.Manifest.permission.INTERACT_ACROSS_USERS} + ) public boolean hasUserRestriction(@UserRestrictionKey String restrictionKey) { - return hasUserRestrictionForUser(restrictionKey, Process.myUserHandle()); + return hasUserRestrictionForUser(restrictionKey, getContextUserIfAppropriate()); } /** @@ -2780,8 +2923,13 @@ public class UserManager { * or setting certain settings. * @param restrictionKey the string key representing the restriction * @param userHandle the UserHandle of the user for whom to retrieve the restrictions. + * @deprecated Use {@link #hasUserRestrictionForUser(String, UserHandle)} instead. */ @UnsupportedAppUsage + @RequiresPermission(anyOf = { + android.Manifest.permission.MANAGE_USERS, + android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true) + @Deprecated public boolean hasUserRestriction(@UserRestrictionKey String restrictionKey, UserHandle userHandle) { return hasUserRestrictionForUser(restrictionKey, userHandle); @@ -2805,8 +2953,16 @@ public class UserManager { android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true) public boolean hasUserRestrictionForUser(@NonNull @UserRestrictionKey String restrictionKey, @NonNull UserHandle userHandle) { + return hasUserRestrictionForUser(restrictionKey, userHandle.getIdentifier()); + } + + @RequiresPermission(anyOf = { + android.Manifest.permission.MANAGE_USERS, + android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true) + private boolean hasUserRestrictionForUser(@NonNull @UserRestrictionKey String restrictionKey, + @UserIdInt int userId) { try { - return mService.hasUserRestriction(restrictionKey, userHandle.getIdentifier()); + return mService.hasUserRestriction(restrictionKey, userId); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } @@ -3148,7 +3304,7 @@ public class UserManager { /** * Creates a restricted profile with the specified name. This method also sets necessary - * restrictions and adds shared accounts. + * restrictions and adds shared accounts (with the context user). * * @param name profile's name * @return the {@link UserInfo} object for the created user, or {@code null} if the user @@ -3159,16 +3315,17 @@ public class UserManager { @TestApi @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS, Manifest.permission.CREATE_USERS}) + @UserHandleAware() public @Nullable UserInfo createRestrictedProfile(@Nullable String name) { try { - UserHandle parentUserHandle = Process.myUserHandle(); - UserInfo user = mService.createRestrictedProfileWithThrow(name, - parentUserHandle.getIdentifier()); - if (user != null) { + final int parentUserId = mUserId; + final UserInfo profile = mService.createRestrictedProfileWithThrow(name, parentUserId); + if (profile != null) { + final UserHandle parentUserHandle = UserHandle.of(parentUserId); AccountManager.get(mContext).addSharedAccountsFromParentUser(parentUserHandle, - UserHandle.of(user.id)); + UserHandle.of(profile.id)); } - return user; + return profile; } catch (ServiceSpecificException e) { return null; } catch (RemoteException re) { @@ -3260,13 +3417,14 @@ public class UserManager { /** * @hide * - * Returns the preferred account name for user creation. + * Returns the preferred account name for the context user's creation. */ @SystemApi @RequiresPermission(android.Manifest.permission.MANAGE_USERS) + @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU) public String getSeedAccountName() { try { - return mService.getSeedAccountName(); + return mService.getSeedAccountName(getContextUserIfAppropriate()); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } @@ -3275,13 +3433,14 @@ public class UserManager { /** * @hide * - * Returns the preferred account type for user creation. + * Returns the preferred account type for the context user's creation. */ @SystemApi @RequiresPermission(android.Manifest.permission.MANAGE_USERS) + @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU) public String getSeedAccountType() { try { - return mService.getSeedAccountType(); + return mService.getSeedAccountType(getContextUserIfAppropriate()); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } @@ -3291,13 +3450,14 @@ public class UserManager { * @hide * * Returns the preferred account's options bundle for user creation. - * @return Any options set by the requestor that created the user. + * @return Any options set by the requestor that created the context user. */ @SystemApi @RequiresPermission(android.Manifest.permission.MANAGE_USERS) + @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU) public PersistableBundle getSeedAccountOptions() { try { - return mService.getSeedAccountOptions(); + return mService.getSeedAccountOptions(getContextUserIfAppropriate()); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } @@ -3327,13 +3487,14 @@ public class UserManager { /** * @hide - * Clears the seed information used to create this user. + * Clears the seed information used to create the context user. */ @SystemApi @RequiresPermission(android.Manifest.permission.MANAGE_USERS) + @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU) public void clearSeedAccountData() { try { - mService.clearSeedAccountData(); + mService.clearSeedAccountData(getContextUserIfAppropriate()); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } @@ -3657,8 +3818,8 @@ public class UserManager { * {@link #getEnabledProfiles(int)} if you need only the enabled ones. *

    Note that this includes all profile types (not including Restricted profiles). * - *

    Requires {@link android.Manifest.permission#MANAGE_USERS}. - * {@link android.Manifest.permission#CREATE_USERS} suffices if userId is the calling user. + *

    Requires {@link android.Manifest.permission#MANAGE_USERS} or + * {@link android.Manifest.permission#CREATE_USERS} if userId is not the calling user. * @param userId profiles of this user will be returned. * @return the list of profiles. * @hide @@ -3710,8 +3871,8 @@ public class UserManager { * Note that this returns only enabled. *

    Note that this includes all profile types (not including Restricted profiles). * - *

    Requires {@link android.Manifest.permission#MANAGE_USERS}. - * {@link android.Manifest.permission#CREATE_USERS} suffices if userId is the calling user. + *

    Requires {@link android.Manifest.permission#MANAGE_USERS} or + * {@link android.Manifest.permission#CREATE_USERS} if userId is not the calling user. * @param userId profiles of this user will be returned. * @return the list of profiles. * @hide @@ -3728,19 +3889,21 @@ public class UserManager { } /** - * Returns a list of UserHandles for profiles associated with the user that the calling process - * is running on, including the user itself. + * Returns a list of UserHandles for profiles associated with the context user, including the + * user itself. *

    Note that this includes all profile types (not including Restricted profiles). * - * @return A non-empty list of UserHandles associated with the calling user. + * @return A non-empty list of UserHandles associated with the context user. */ + @UserHandleAware( + enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU, + requiresAnyOfPermissionsIfNotCaller = { + Manifest.permission.MANAGE_USERS, + Manifest.permission.CREATE_USERS} + ) public List getUserProfiles() { - int[] userIds = getProfileIds(UserHandle.myUserId(), true /* enabledOnly */); - List result = new ArrayList<>(userIds.length); - for (int userId : userIds) { - result.add(UserHandle.of(userId)); - } - return result; + int[] userIds = getProfileIds(getContextUserIfAppropriate(), true /* enabledOnly */); + return convertUserIdsToUserHandles(userIds); } /** @@ -3748,7 +3911,7 @@ public class UserManager { * user itself. *

    Note that this includes all profile types (not including Restricted profiles). * - * @return A non-empty list of UserHandles associated with the calling user. + * @return A non-empty list of UserHandles associated with the context user. * @hide */ @SystemApi @@ -3764,7 +3927,7 @@ public class UserManager { * itself. *

    Note that this includes all profile types (not including Restricted profiles). * - * @return A non-empty list of UserHandles associated with the calling user. + * @return A non-empty list of UserHandles associated with the context user. * @hide */ @SystemApi @@ -3781,13 +3944,18 @@ public class UserManager { *

    Note that this includes all profile types (not including Restricted profiles). * * @param enabledOnly whether to return only {@link UserInfo#isEnabled() enabled} profiles - * @return A non-empty list of UserHandles associated with the calling user. + * @return A non-empty list of UserHandles associated with the context user. */ @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS, Manifest.permission.CREATE_USERS}, conditional = true) @UserHandleAware private @NonNull List getProfiles(boolean enabledOnly) { final int[] userIds = getProfileIds(mUserId, enabledOnly); + return convertUserIdsToUserHandles(userIds); + } + + /** Converts the given array of userIds to a List of UserHandles. */ + private @NonNull List convertUserIdsToUserHandles(@NonNull int[] userIds) { final List result = new ArrayList<>(userIds.length); for (int userId : userIds) { result.add(UserHandle.of(userId)); @@ -4433,6 +4601,11 @@ public class UserManager { * @return true if user switcher is enabled * @hide */ + @RequiresPermission(anyOf = { + android.Manifest.permission.MANAGE_USERS, + android.Manifest.permission.CREATE_USERS // And INTERACT_ if diff profile group + }) + @UserHandleAware() public boolean isUserSwitcherEnabled() { return isUserSwitcherEnabled(true); } @@ -4445,11 +4618,16 @@ public class UserManager { * @return true if user switcher should be shown. * @hide */ + @RequiresPermission(anyOf = { + android.Manifest.permission.MANAGE_USERS, + android.Manifest.permission.CREATE_USERS // And INTERACT_ if diff profile group + }) + @UserHandleAware() public boolean isUserSwitcherEnabled(boolean showEvenIfNotActionable) { if (!supportsMultipleUsers()) { return false; } - if (hasUserRestriction(DISALLOW_USER_SWITCH)) { + if (hasUserRestrictionForUser(DISALLOW_USER_SWITCH, mUserId)) { return false; } // If Demo Mode is on, don't show user switcher @@ -4468,10 +4646,14 @@ public class UserManager { // The feature is enabled. But is it worth showing? return showEvenIfNotActionable || areThereUsersToWhichToSwitch() // There are switchable users. - || !hasUserRestriction(UserManager.DISALLOW_ADD_USER); // New users can be added. + || !hasUserRestrictionForUser(DISALLOW_ADD_USER, mUserId); // New users can be added } /** Returns whether there are any users (other than the current user) to which to switch. */ + @RequiresPermission(anyOf = { + android.Manifest.permission.MANAGE_USERS, + android.Manifest.permission.CREATE_USERS + }) private boolean areThereUsersToWhichToSwitch() { final List users = getAliveUsers(); if (users == null) { @@ -4530,8 +4712,8 @@ public class UserManager { } /** - * Returns a {@link Bundle} containing any saved application restrictions for this user, for the - * given package name. Only an application with this package name can call this method. + * Returns a {@link Bundle} containing any saved application restrictions for the context user, + * for the given package name. Only an application with this package name can call this method. * *

    The returned {@link Bundle} consists of key-value pairs, as defined by the application, * where the types of values may be: @@ -4551,9 +4733,11 @@ public class UserManager { * @see #KEY_RESTRICTIONS_PENDING */ @WorkerThread + @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU) public Bundle getApplicationRestrictions(String packageName) { try { - return mService.getApplicationRestrictions(packageName); + return mService.getApplicationRestrictionsForUser(packageName, + getContextUserIfAppropriate()); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index e8182e07ba4d2..eb94fc447d891 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -858,11 +858,12 @@ public class UserManagerService extends IUserManager.Stub { @Override public List getProfiles(@UserIdInt int userId, boolean enabledOnly) { - boolean returnFullInfo = true; + boolean returnFullInfo; if (userId != UserHandle.getCallingUserId()) { checkManageOrCreateUsersPermission("getting profiles related to user " + userId); + returnFullInfo = true; } else { - returnFullInfo = hasManageUsersPermission(); + returnFullInfo = hasManageOrCreateUsersPermission(); } final long ident = Binder.clearCallingIdentity(); try { @@ -1659,9 +1660,13 @@ public class UserManagerService extends IUserManager.Stub { } @Override - public boolean isRestricted() { + public boolean isRestricted(@UserIdInt int userId) { + if (userId != UserHandle.getCallingUserId()) { + checkManageOrCreateUsersPermission("query isRestricted for user " + userId); + } synchronized (mUsersLock) { - return getUserInfoLU(UserHandle.getCallingUserId()).isRestricted(); + final UserInfo userInfo = getUserInfoLU(userId); + return userInfo == null ? false : userInfo.isRestricted(); } } @@ -1682,15 +1687,14 @@ public class UserManagerService extends IUserManager.Stub { } @Override - public boolean hasRestrictedProfiles() { + public boolean hasRestrictedProfiles(@UserIdInt int userId) { checkManageUsersPermission("hasRestrictedProfiles"); - final int callingUserId = UserHandle.getCallingUserId(); synchronized (mUsersLock) { final int userSize = mUsers.size(); for (int i = 0; i < userSize; i++) { UserInfo profile = mUsers.valueAt(i).info; - if (callingUserId != profile.id - && profile.restrictedProfileParentId == callingUserId) { + if (userId != profile.id + && profile.restrictedProfileParentId == userId) { return true; } } @@ -2541,6 +2545,14 @@ public class UserManagerService extends IUserManager.Stub { */ private static final boolean hasManageUsersPermission() { final int callingUid = Binder.getCallingUid(); + return hasManageUsersPermission(callingUid); + } + + /** + * @return whether the given UID is system UID or root's UID or the has the permission + * {@link android.Manifest.permission#MANAGE_USERS MANAGE_USERS}. + */ + private static boolean hasManageUsersPermission(int callingUid) { return UserHandle.isSameApp(callingUid, Process.SYSTEM_UID) || callingUid == Process.ROOT_UID || hasPermissionGranted(android.Manifest.permission.MANAGE_USERS, callingUid); @@ -2552,9 +2564,7 @@ public class UserManagerService extends IUserManager.Stub { */ private static final boolean hasManageUsersOrPermission(String alternativePermission) { final int callingUid = Binder.getCallingUid(); - return UserHandle.isSameApp(callingUid, Process.SYSTEM_UID) - || callingUid == Process.ROOT_UID - || hasPermissionGranted(android.Manifest.permission.MANAGE_USERS, callingUid) + return hasManageUsersPermission(callingUid) || hasPermissionGranted(alternativePermission, callingUid); } @@ -4939,39 +4949,39 @@ public class UserManagerService extends IUserManager.Stub { } @Override - public String getSeedAccountName() throws RemoteException { + public String getSeedAccountName(@UserIdInt int userId) throws RemoteException { checkManageUsersPermission("Cannot get seed account information"); synchronized (mUsersLock) { - UserData userData = getUserDataLU(UserHandle.getCallingUserId()); - return userData.seedAccountName; + final UserData userData = getUserDataLU(userId); + return userData == null ? null : userData.seedAccountName; } } @Override - public String getSeedAccountType() throws RemoteException { + public String getSeedAccountType(@UserIdInt int userId) throws RemoteException { checkManageUsersPermission("Cannot get seed account information"); synchronized (mUsersLock) { - UserData userData = getUserDataLU(UserHandle.getCallingUserId()); - return userData.seedAccountType; + final UserData userData = getUserDataLU(userId); + return userData == null ? null : userData.seedAccountType; } } @Override - public PersistableBundle getSeedAccountOptions() throws RemoteException { + public PersistableBundle getSeedAccountOptions(@UserIdInt int userId) throws RemoteException { checkManageUsersPermission("Cannot get seed account information"); synchronized (mUsersLock) { - UserData userData = getUserDataLU(UserHandle.getCallingUserId()); - return userData.seedAccountOptions; + final UserData userData = getUserDataLU(userId); + return userData == null ? null : userData.seedAccountOptions; } } @Override - public void clearSeedAccountData() throws RemoteException { + public void clearSeedAccountData(@UserIdInt int userId) throws RemoteException { checkManageUsersPermission("Cannot clear seed account information"); synchronized (mPackagesLock) { UserData userData; synchronized (mUsersLock) { - userData = getUserDataLU(UserHandle.getCallingUserId()); + userData = getUserDataLU(userId); if (userData == null) return; userData.clearSeedAccountData(); }