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:
+ *
+ * -
+ * if the {@literal @}UserHandleAware specifies a
+ * {@link android.annotation.UserHandleAware#enabledSinceTargetSdkVersion} of
+ * {@link Build.VERSION_CODES#TIRAMISU} the calling user is used.
+ *
+ * -
+ * if the {@literal @}UserHandleAware doesn't specify a
+ * {@link android.annotation.UserHandleAware#enabledSinceTargetSdkVersion}, the
+ * context user is used.
+ *
+ * - there should currently be no other values used by UserManager for
+ * {@link android.annotation.UserHandleAware#enabledSinceTargetSdkVersion}, since all
+ * old implicitly user-dependant APIs were updated in that version and anything
+ * introduced more recently should already be {@literal @}UserHandleAware.
+ *
+ *
+ *
+ * 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();
}