Merge "Refactored user switch workflow to decouple UserInfo from TargetUser."
This commit is contained in:
committed by
Android (Google) Code Review
commit
afc4ef7cdc
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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(')');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user