diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java index 663fd6259fd44..ad85784ca066c 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java @@ -360,7 +360,7 @@ public final class AutofillManagerService @Override // from SystemService public boolean isUserSupported(TargetUser user) { - return user.getUserInfo().isFull() || user.getUserInfo().isManagedProfile(); + return user.isFull() || user.isManagedProfile(); } @Override // from SystemService diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java index ea94ad0b3c202..e742015916e7d 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java @@ -219,7 +219,7 @@ public final class ContentCaptureManagerService extends @Override // from SystemService public boolean isUserSupported(TargetUser user) { - return user.getUserInfo().isFull() || user.getUserInfo().isManagedProfile(); + return user.isFull() || user.isManagedProfile(); } @Override // from SystemService diff --git a/services/core/java/android/os/UserManagerInternal.java b/services/core/java/android/os/UserManagerInternal.java index fbe8c04bd59cd..61e8128bd5106 100644 --- a/services/core/java/android/os/UserManagerInternal.java +++ b/services/core/java/android/os/UserManagerInternal.java @@ -262,8 +262,7 @@ public abstract class UserManagerInternal { public abstract boolean hasUserRestriction(String restriction, int userId); /** - * Gets an {@link UserInfo} for the given {@code userId}, or {@code null} if not - * found. + * Gets a {@link UserInfo} for the given {@code userId}, or {@code null} if not found. */ public abstract @Nullable UserInfo getUserInfo(@UserIdInt int userId); diff --git a/services/core/java/com/android/server/SystemService.java b/services/core/java/com/android/server/SystemService.java index 1496e926b95bc..84d01ec3598de 100644 --- a/services/core/java/com/android/server/SystemService.java +++ b/services/core/java/com/android/server/SystemService.java @@ -23,6 +23,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.annotation.SystemApi.Client; +import android.annotation.UserIdInt; import android.app.ActivityThread; import android.content.Context; import android.content.pm.UserInfo; @@ -131,45 +132,89 @@ public abstract class SystemService { */ @SystemApi(client = Client.SYSTEM_SERVER) public static final class TargetUser { - @NonNull - private final UserInfo mUserInfo; + + // NOTE: attributes below must be immutable while ther user is running (i.e., from the + // moment it's started until after it's shutdown). + private final @UserIdInt int mUserId; + private final boolean mFull; + private final boolean mManagedProfile; + private final boolean mPreCreated; /** @hide */ public TargetUser(@NonNull UserInfo userInfo) { - mUserInfo = userInfo; + mUserId = userInfo.id; + mFull = userInfo.isFull(); + mManagedProfile = userInfo.isManagedProfile(); + mPreCreated = userInfo.preCreated; } /** - * @return The information about the user. NOTE: this is a "live" object - * referenced by {@link UserManagerService} and hence should not be modified. + * Checks if the target user is {@link UserInfo#isFull() full}. * * @hide */ - @NonNull - public UserInfo getUserInfo() { - return mUserInfo; + public boolean isFull() { + return mFull; } /** - * @return the target {@link UserHandle}. + * Checks if the target user is a managed profile. + * + * @hide + */ + public boolean isManagedProfile() { + return mManagedProfile; + } + + /** + * Checks if the target user is a pre-created user. + * + * @hide + */ + public boolean isPreCreated() { + return mPreCreated; + } + + /** + * Gets the target user's {@link UserHandle}. */ @NonNull public UserHandle getUserHandle() { - return mUserInfo.getUserHandle(); + return UserHandle.of(mUserId); } /** - * @return the integer user id + * Gets the target user's id. * * @hide */ - public int getUserIdentifier() { - return mUserInfo.id; + public @UserIdInt int getUserIdentifier() { + return mUserId; } @Override public String toString() { - return Integer.toString(getUserIdentifier()); + return Integer.toString(mUserId); + } + + /** + * @hide + */ + public void dump(@NonNull StringBuilder builder) { + builder.append(getUserIdentifier()); + + if (!isFull() && !isManagedProfile()) return; + + builder.append('('); + boolean addComma = false; + if (isFull()) { + builder.append("full"); + } + if (isManagedProfile()) { + if (addComma) builder.append(','); + builder.append("mp"); + } + builder.append(')'); } } diff --git a/services/core/java/com/android/server/SystemServiceManager.java b/services/core/java/com/android/server/SystemServiceManager.java index 74bb7d7e90f18..df966c1ea6ac4 100644 --- a/services/core/java/com/android/server/SystemServiceManager.java +++ b/services/core/java/com/android/server/SystemServiceManager.java @@ -27,7 +27,10 @@ import android.os.UserHandle; import android.os.UserManagerInternal; import android.util.ArrayMap; import android.util.Slog; +import android.util.SparseArray; +import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.Preconditions; import com.android.server.SystemService.TargetUser; import com.android.server.utils.TimingsTraceAndSlog; @@ -74,6 +77,13 @@ public class SystemServiceManager { private UserManagerInternal mUserManagerInternal; + /** + * Map of started {@link TargetUser TargetUsers} by user id; users are added on start and + * removed after they're completely shut down. + */ + @GuardedBy("mTargetUsers") + private final SparseArray mTargetUsers = new SparseArray<>(); + SystemServiceManager(Context context) { mContext = context; } @@ -240,21 +250,26 @@ public class SystemServiceManager { mUserManagerInternal = LocalServices.getService(UserManagerInternal.class); } - private @NonNull UserInfo getUserInfo(@UserIdInt int userHandle) { - if (mUserManagerInternal == null) { - throw new IllegalStateException("mUserManagerInternal not set yet"); + private @NonNull TargetUser getTargetUser(@UserIdInt int userHandle) { + final TargetUser targetUser; + synchronized (mTargetUsers) { + targetUser = mTargetUsers.get(userHandle); } - final UserInfo userInfo = mUserManagerInternal.getUserInfo(userHandle); - if (userInfo == null) { - throw new IllegalStateException("No UserInfo for " + userHandle); - } - return userInfo; + Preconditions.checkState(targetUser != null, "No TargetUser for " + userHandle); + return targetUser; } /** * Starts the given user. */ public void startUser(final @NonNull TimingsTraceAndSlog t, final @UserIdInt int userHandle) { + // Create cached TargetUser + final UserInfo userInfo = mUserManagerInternal.getUserInfo(userHandle); + Preconditions.checkState(userInfo != null, "No UserInfo for " + userHandle); + synchronized (mTargetUsers) { + mTargetUsers.put(userHandle, new TargetUser(userInfo)); + } + onUser(t, START, userHandle); } @@ -291,6 +306,11 @@ public class SystemServiceManager { */ public void cleanupUser(final @UserIdInt int userHandle) { onUser(CLEANUP, userHandle); + + // Remove cached TargetUser + synchronized (mTargetUsers) { + mTargetUsers.remove(userHandle); + } } private void onUser(@NonNull String onWhat, @UserIdInt int userHandle) { @@ -306,9 +326,9 @@ public class SystemServiceManager { @UserIdInt int curUserId, @UserIdInt int prevUserId) { t.traceBegin("ssm." + onWhat + "User-" + curUserId); Slog.i(TAG, "Calling on" + onWhat + "User " + curUserId); - final TargetUser curUser = new TargetUser(getUserInfo(curUserId)); + final TargetUser curUser = getTargetUser(curUserId); final TargetUser prevUser = prevUserId == UserHandle.USER_NULL ? null - : new TargetUser(getUserInfo(prevUserId)); + : getTargetUser(prevUserId); final int serviceLen = mServices.size(); for (int i = 0; i < serviceLen; i++) { final SystemService service = mServices.get(i); @@ -437,7 +457,7 @@ public class SystemServiceManager { */ public void dump() { StringBuilder builder = new StringBuilder(); - builder.append("Current phase: ").append(mCurrentPhase).append("\n"); + builder.append("Current phase: ").append(mCurrentPhase).append('\n'); builder.append("Services:\n"); final int startedLen = mServices.size(); for (int i = 0; i < startedLen; i++) { @@ -447,6 +467,14 @@ public class SystemServiceManager { .append("\n"); } + builder.append("Target users: "); + final int targetUsersSize = mTargetUsers.size(); + for (int i = 0; i < targetUsersSize; i++) { + mTargetUsers.valueAt(i).dump(builder); + if (i != targetUsersSize - 1) builder.append(','); + } + builder.append('\n'); + Slog.e(TAG, builder.toString()); } } diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index 7420e0a6e8283..83bf28e2d3bae 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -988,6 +988,8 @@ class UserController implements Handler.Callback { final ArrayList stopCallbacks; final ArrayList keyEvictedCallbacks; int userIdToLock = userId; + // Must get a reference to UserInfo before it's removed + final UserInfo userInfo = getUserInfo(userId); synchronized (mLock) { stopCallbacks = new ArrayList<>(uss.mStopCallbacks); keyEvictedCallbacks = new ArrayList<>(uss.mKeyEvictedCallbacks); @@ -1030,8 +1032,8 @@ class UserController implements Handler.Callback { if (stopped) { mInjector.systemServiceManagerCleanupUser(userId); mInjector.stackSupervisorRemoveUser(userId); + // Remove the user if it is ephemeral. - UserInfo userInfo = getUserInfo(userId); if (userInfo.isEphemeral() && !userInfo.preCreated) { mInjector.getUserManager().removeUserEvenWhenDisallowed(userId); } diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index f66b4ee5a1a73..d137fd05f793f 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -499,20 +499,23 @@ public class UserManagerService extends IUserManager.Stub { states = new SparseIntArray(); invalidateIsUserUnlockedCache(); } - public int get(int userId) { + public int get(@UserIdInt int userId) { return states.get(userId); } - public int get(int userId, int fallback) { + public int get(@UserIdInt int userId, int fallback) { return states.indexOfKey(userId) >= 0 ? states.get(userId) : fallback; } - public void put(int userId, int state) { + public void put(@UserIdInt int userId, int state) { states.put(userId, state); invalidateIsUserUnlockedCache(); } - public void delete(int userId) { + public void delete(@UserIdInt int userId) { states.delete(userId); invalidateIsUserUnlockedCache(); } + public boolean has(@UserIdInt int userId) { + return states.get(userId, UserHandle.USER_NULL) != UserHandle.USER_NULL; + } @Override public String toString() { return states.toString(); @@ -3561,6 +3564,13 @@ public class UserManagerService extends IUserManager.Stub { if (preCreatedUserData == null) { return null; } + synchronized (mUserStates) { + if (mUserStates.has(preCreatedUserData.info.id)) { + Slog.w(LOG_TAG, "Cannot reuse pre-created user " + + preCreatedUserData.info.id + " because it didn't stop yet"); + return null; + } + } final UserInfo preCreatedUser = preCreatedUserData.info; final int newFlags = preCreatedUser.flags | flags; if (!checkUserTypeConsistency(newFlags)) { @@ -5225,6 +5235,7 @@ public class UserManagerService extends IUserManager.Stub { return userData == null ? null : userData.info; } + @Override public @NonNull UserInfo[] getUserInfos() { synchronized (mUsersLock) { int userSize = mUsers.size(); diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index 78556ef41edb0..1e5d92b270d21 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -61,7 +61,6 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.content.pm.ParceledListSlice; import android.content.pm.ShortcutServiceInternal; -import android.content.pm.UserInfo; import android.content.res.Configuration; import android.os.Binder; import android.os.Environment; @@ -306,25 +305,25 @@ public class UsageStatsService extends SystemService implements @Override public void onUserStopping(@NonNull TargetUser user) { - final UserInfo userInfo = user.getUserInfo(); + final int userId = user.getUserIdentifier(); synchronized (mLock) { // User was started but never unlocked so no need to report a user stopped event - if (!mUserUnlockedStates.get(userInfo.id)) { - persistPendingEventsLocked(userInfo.id); + if (!mUserUnlockedStates.get(userId)) { + persistPendingEventsLocked(userId); return; } // Report a user stopped event before persisting all stats to disk via the user service final Event event = new Event(USER_STOPPED, SystemClock.elapsedRealtime()); event.mPackage = Event.DEVICE_EVENT_PACKAGE_NAME; - reportEvent(event, userInfo.id); - final UserUsageStatsService userService = mUserState.get(userInfo.id); + reportEvent(event, userId); + final UserUsageStatsService userService = mUserState.get(userId); if (userService != null) { userService.userStopped(); } - mUserUnlockedStates.put(userInfo.id, false); - mUserState.put(userInfo.id, null); // release the service (mainly for GC) + mUserUnlockedStates.put(userId, false); + mUserState.put(userId, null); // release the service (mainly for GC) } } diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java index 9ff44eba8fa0e..a2215ceed36af 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java @@ -164,13 +164,13 @@ public class VoiceInteractionManagerService extends SystemService { } } - private boolean isSupported(UserInfo user) { + @Override + public boolean isUserSupported(@NonNull TargetUser user) { return user.isFull(); } - @Override - public boolean isUserSupported(TargetUser user) { - return isSupported(user.getUserInfo()); + private boolean isUserSupported(@NonNull UserInfo user) { + return user.isFull(); } @Override @@ -461,7 +461,7 @@ public class VoiceInteractionManagerService extends SystemService { private void setCurrentUserLocked(@UserIdInt int userHandle) { mCurUser = userHandle; final UserInfo userInfo = mUserManagerInternal.getUserInfo(mCurUser); - mCurUserSupported = isSupported(userInfo); + mCurUserSupported = isUserSupported(userInfo); } public void switchUser(@UserIdInt int userHandle) {