Merge "Refactored user switch workflow to decouple UserInfo from TargetUser."

This commit is contained in:
TreeHugger Robot
2020-08-14 02:54:22 +00:00
committed by Android (Google) Code Review
9 changed files with 131 additions and 47 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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);

View File

@@ -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. <b>NOTE: </b> 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(')');
}
}

View File

@@ -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<TargetUser> 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());
}
}

View File

@@ -988,6 +988,8 @@ class UserController implements Handler.Callback {
final ArrayList<IStopUserCallback> stopCallbacks;
final ArrayList<KeyEvictedCallback> 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);
}

View File

@@ -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();

View File

@@ -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)
}
}

View File

@@ -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) {