Merge "Avoid recycling recently removed user IDs." into jb-mr1.1-dev

This commit is contained in:
Jeff Sharkey
2012-11-12 10:30:00 -08:00
committed by Android (Google) Code Review

View File

@ -16,8 +16,7 @@
package com.android.server.pm; package com.android.server.pm;
import com.android.internal.util.ArrayUtils; import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
import com.android.internal.util.FastXmlSerializer;
import android.app.Activity; import android.app.Activity;
import android.app.ActivityManager; import android.app.ActivityManager;
@ -26,7 +25,6 @@ import android.app.IStopUserCallback;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.pm.UserInfo; import android.content.pm.UserInfo;
import android.graphics.Bitmap; import android.graphics.Bitmap;
@ -34,6 +32,7 @@ import android.graphics.BitmapFactory;
import android.os.Binder; import android.os.Binder;
import android.os.Environment; import android.os.Environment;
import android.os.FileUtils; import android.os.FileUtils;
import android.os.Handler;
import android.os.IUserManager; import android.os.IUserManager;
import android.os.Process; import android.os.Process;
import android.os.RemoteException; import android.os.RemoteException;
@ -42,9 +41,17 @@ import android.os.UserManager;
import android.util.AtomicFile; import android.util.AtomicFile;
import android.util.Slog; import android.util.Slog;
import android.util.SparseArray; import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.TimeUtils; import android.util.TimeUtils;
import android.util.Xml; import android.util.Xml;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastXmlSerializer;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
import java.io.BufferedOutputStream; import java.io.BufferedOutputStream;
import java.io.File; import java.io.File;
import java.io.FileDescriptor; import java.io.FileDescriptor;
@ -54,13 +61,8 @@ import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet;
import java.util.List; import java.util.List;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
public class UserManagerService extends IUserManager.Stub { public class UserManagerService extends IUserManager.Stub {
private static final String LOG_TAG = "UserManagerService"; private static final String LOG_TAG = "UserManagerService";
@ -95,19 +97,24 @@ public class UserManagerService extends IUserManager.Stub {
private final Object mInstallLock; private final Object mInstallLock;
private final Object mPackagesLock; private final Object mPackagesLock;
private final Handler mHandler;
private final File mUsersDir; private final File mUsersDir;
private final File mUserListFile; private final File mUserListFile;
private final File mBaseUserPath; private final File mBaseUserPath;
private SparseArray<UserInfo> mUsers = new SparseArray<UserInfo>(); private final SparseArray<UserInfo> mUsers = new SparseArray<UserInfo>();
private HashSet<Integer> mRemovingUserIds = new HashSet<Integer>();
/**
* Set of user IDs being actively removed. Removed IDs linger in this set
* for several seconds to work around a VFS caching issue.
*/
// @GuardedBy("mPackagesLock")
private final SparseBooleanArray mRemovingUserIds = new SparseBooleanArray();
private int[] mUserIds; private int[] mUserIds;
private boolean mGuestEnabled; private boolean mGuestEnabled;
private int mNextSerialNumber; private int mNextSerialNumber;
// This resets on a reboot. Otherwise it keeps incrementing so that user ids are
// not reused in quick succession
private int mNextUserId = MIN_USER_ID;
private int mUserVersion = 0; private int mUserVersion = 0;
private static UserManagerService sInstance; private static UserManagerService sInstance;
@ -147,6 +154,7 @@ public class UserManagerService extends IUserManager.Stub {
mPm = pm; mPm = pm;
mInstallLock = installLock; mInstallLock = installLock;
mPackagesLock = packagesLock; mPackagesLock = packagesLock;
mHandler = new Handler();
synchronized (mInstallLock) { synchronized (mInstallLock) {
synchronized (mPackagesLock) { synchronized (mPackagesLock) {
mUsersDir = new File(dataDir, USER_INFO_DIR); mUsersDir = new File(dataDir, USER_INFO_DIR);
@ -190,7 +198,7 @@ public class UserManagerService extends IUserManager.Stub {
if (ui.partial) { if (ui.partial) {
continue; continue;
} }
if (!excludeDying || !mRemovingUserIds.contains(ui.id)) { if (!excludeDying || !mRemovingUserIds.get(ui.id)) {
users.add(ui); users.add(ui);
} }
} }
@ -212,7 +220,7 @@ public class UserManagerService extends IUserManager.Stub {
private UserInfo getUserInfoLocked(int userId) { private UserInfo getUserInfoLocked(int userId) {
UserInfo ui = mUsers.get(userId); UserInfo ui = mUsers.get(userId);
// If it is partial and not in the process of being removed, return as unknown user. // If it is partial and not in the process of being removed, return as unknown user.
if (ui != null && ui.partial && !mRemovingUserIds.contains(userId)) { if (ui != null && ui.partial && !mRemovingUserIds.get(userId)) {
Slog.w(LOG_TAG, "getUserInfo: unknown user #" + userId); Slog.w(LOG_TAG, "getUserInfo: unknown user #" + userId);
return null; return null;
} }
@ -502,7 +510,7 @@ public class UserManagerService extends IUserManager.Stub {
private void fallbackToSingleUserLocked() { private void fallbackToSingleUserLocked() {
// Create the primary user // Create the primary user
UserInfo primary = new UserInfo(0, UserInfo primary = new UserInfo(0,
mContext.getResources().getString(com.android.internal.R.string.owner_name), null, mContext.getResources().getString(com.android.internal.R.string.owner_name), null,
UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY | UserInfo.FLAG_INITIALIZED); UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY | UserInfo.FLAG_INITIALIZED);
mUsers.put(0, primary); mUsers.put(0, primary);
@ -749,7 +757,7 @@ public class UserManagerService extends IUserManager.Stub {
if (userHandle == 0 || user == null) { if (userHandle == 0 || user == null) {
return false; return false;
} }
mRemovingUserIds.add(userHandle); mRemovingUserIds.put(userHandle, true);
// Set this to a partially created user, so that the user will be purged // Set this to a partially created user, so that the user will be purged
// on next startup, in case the runtime stops now before stopping and // on next startup, in case the runtime stops now before stopping and
// removing the user completely. // removing the user completely.
@ -813,13 +821,25 @@ public class UserManagerService extends IUserManager.Stub {
} }
} }
private void removeUserStateLocked(int userHandle) { private void removeUserStateLocked(final int userHandle) {
// Cleanup package manager settings // Cleanup package manager settings
mPm.cleanUpUserLILPw(userHandle); mPm.cleanUpUserLILPw(userHandle);
// Remove this user from the list // Remove this user from the list
mUsers.remove(userHandle); mUsers.remove(userHandle);
mRemovingUserIds.remove(userHandle);
// Have user ID linger for several seconds to let external storage VFS
// cache entries expire. This must be greater than the 'entry_valid'
// timeout used by the FUSE daemon.
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
synchronized (mPackagesLock) {
mRemovingUserIds.delete(userHandle);
}
}
}, MINUTE_IN_MILLIS);
// Remove user file // Remove user file
AtomicFile userFile = new AtomicFile(new File(mUsersDir, userHandle + ".xml")); AtomicFile userFile = new AtomicFile(new File(mUsersDir, userHandle + ".xml"));
userFile.delete(); userFile.delete();
@ -906,14 +926,13 @@ public class UserManagerService extends IUserManager.Stub {
*/ */
private int getNextAvailableIdLocked() { private int getNextAvailableIdLocked() {
synchronized (mPackagesLock) { synchronized (mPackagesLock) {
int i = mNextUserId; int i = MIN_USER_ID;
while (i < Integer.MAX_VALUE) { while (i < Integer.MAX_VALUE) {
if (mUsers.indexOfKey(i) < 0 && !mRemovingUserIds.contains(i)) { if (mUsers.indexOfKey(i) < 0 && !mRemovingUserIds.get(i)) {
break; break;
} }
i++; i++;
} }
mNextUserId = i + 1;
return i; return i;
} }
} }
@ -938,7 +957,7 @@ public class UserManagerService extends IUserManager.Stub {
UserInfo user = mUsers.valueAt(i); UserInfo user = mUsers.valueAt(i);
if (user == null) continue; if (user == null) continue;
pw.print(" "); pw.print(user); pw.print(" serialNo="); pw.print(user.serialNumber); pw.print(" "); pw.print(user); pw.print(" serialNo="); pw.print(user.serialNumber);
if (mRemovingUserIds.contains(mUsers.keyAt(i))) pw.print(" <removing> "); if (mRemovingUserIds.get(mUsers.keyAt(i))) pw.print(" <removing> ");
if (user.partial) pw.print(" <partial>"); if (user.partial) pw.print(" <partial>");
pw.println(); pw.println();
pw.print(" Created: "); pw.print(" Created: ");