1616
1717package com .android .server .pm ;
1818
19- import com .android .internal .util .ArrayUtils ;
20- import com .android .internal .util .FastXmlSerializer ;
19+ import static android .text .format .DateUtils .MINUTE_IN_MILLIS ;
2120
2221import android .app .Activity ;
2322import android .app .ActivityManager ;
2625import android .content .BroadcastReceiver ;
2726import android .content .Context ;
2827import android .content .Intent ;
29- import android .content .SharedPreferences ;
3028import android .content .pm .PackageManager ;
3129import android .content .pm .UserInfo ;
3230import android .graphics .Bitmap ;
3331import android .graphics .BitmapFactory ;
3432import android .os .Binder ;
3533import android .os .Environment ;
3634import android .os .FileUtils ;
35+ import android .os .Handler ;
3736import android .os .IUserManager ;
3837import android .os .Process ;
3938import android .os .RemoteException ;
4241import android .util .AtomicFile ;
4342import android .util .Slog ;
4443import android .util .SparseArray ;
44+ import android .util .SparseBooleanArray ;
4545import android .util .TimeUtils ;
4646import android .util .Xml ;
4747
48+ import com .android .internal .util .ArrayUtils ;
49+ import com .android .internal .util .FastXmlSerializer ;
50+
51+ import org .xmlpull .v1 .XmlPullParser ;
52+ import org .xmlpull .v1 .XmlPullParserException ;
53+ import org .xmlpull .v1 .XmlSerializer ;
54+
4855import java .io .BufferedOutputStream ;
4956import java .io .File ;
5057import java .io .FileDescriptor ;
5461import java .io .IOException ;
5562import java .io .PrintWriter ;
5663import java .util .ArrayList ;
57- import java .util .HashSet ;
5864import java .util .List ;
5965
60- import org .xmlpull .v1 .XmlPullParser ;
61- import org .xmlpull .v1 .XmlPullParserException ;
62- import org .xmlpull .v1 .XmlSerializer ;
63-
6466public class UserManagerService extends IUserManager .Stub {
6567
6668 private static final String LOG_TAG = "UserManagerService" ;
@@ -95,19 +97,24 @@ public class UserManagerService extends IUserManager.Stub {
9597 private final Object mInstallLock ;
9698 private final Object mPackagesLock ;
9799
100+ private final Handler mHandler ;
101+
98102 private final File mUsersDir ;
99103 private final File mUserListFile ;
100104 private final File mBaseUserPath ;
101105
102- private SparseArray <UserInfo > mUsers = new SparseArray <UserInfo >();
103- private HashSet <Integer > mRemovingUserIds = new HashSet <Integer >();
106+ private final SparseArray <UserInfo > mUsers = new SparseArray <UserInfo >();
107+
108+ /**
109+ * Set of user IDs being actively removed. Removed IDs linger in this set
110+ * for several seconds to work around a VFS caching issue.
111+ */
112+ // @GuardedBy("mPackagesLock")
113+ private final SparseBooleanArray mRemovingUserIds = new SparseBooleanArray ();
104114
105115 private int [] mUserIds ;
106116 private boolean mGuestEnabled ;
107117 private int mNextSerialNumber ;
108- // This resets on a reboot. Otherwise it keeps incrementing so that user ids are
109- // not reused in quick succession
110- private int mNextUserId = MIN_USER_ID ;
111118 private int mUserVersion = 0 ;
112119
113120 private static UserManagerService sInstance ;
@@ -147,6 +154,7 @@ private UserManagerService(Context context, PackageManagerService pm,
147154 mPm = pm ;
148155 mInstallLock = installLock ;
149156 mPackagesLock = packagesLock ;
157+ mHandler = new Handler ();
150158 synchronized (mInstallLock ) {
151159 synchronized (mPackagesLock ) {
152160 mUsersDir = new File (dataDir , USER_INFO_DIR );
@@ -190,7 +198,7 @@ public List<UserInfo> getUsers(boolean excludeDying) {
190198 if (ui .partial ) {
191199 continue ;
192200 }
193- if (!excludeDying || !mRemovingUserIds .contains (ui .id )) {
201+ if (!excludeDying || !mRemovingUserIds .get (ui .id )) {
194202 users .add (ui );
195203 }
196204 }
@@ -212,7 +220,7 @@ public UserInfo getUserInfo(int userId) {
212220 private UserInfo getUserInfoLocked (int userId ) {
213221 UserInfo ui = mUsers .get (userId );
214222 // If it is partial and not in the process of being removed, return as unknown user.
215- if (ui != null && ui .partial && !mRemovingUserIds .contains (userId )) {
223+ if (ui != null && ui .partial && !mRemovingUserIds .get (userId )) {
216224 Slog .w (LOG_TAG , "getUserInfo: unknown user #" + userId );
217225 return null ;
218226 }
@@ -502,7 +510,7 @@ private void upgradeIfNecessary() {
502510
503511 private void fallbackToSingleUserLocked () {
504512 // Create the primary user
505- UserInfo primary = new UserInfo (0 ,
513+ UserInfo primary = new UserInfo (0 ,
506514 mContext .getResources ().getString (com .android .internal .R .string .owner_name ), null ,
507515 UserInfo .FLAG_ADMIN | UserInfo .FLAG_PRIMARY | UserInfo .FLAG_INITIALIZED );
508516 mUsers .put (0 , primary );
@@ -749,7 +757,7 @@ public boolean removeUser(int userHandle) {
749757 if (userHandle == 0 || user == null ) {
750758 return false ;
751759 }
752- mRemovingUserIds .add (userHandle );
760+ mRemovingUserIds .put (userHandle , true );
753761 // Set this to a partially created user, so that the user will be purged
754762 // on next startup, in case the runtime stops now before stopping and
755763 // removing the user completely.
@@ -813,13 +821,25 @@ public void run() {
813821 }
814822 }
815823
816- private void removeUserStateLocked (int userHandle ) {
824+ private void removeUserStateLocked (final int userHandle ) {
817825 // Cleanup package manager settings
818826 mPm .cleanUpUserLILPw (userHandle );
819827
820828 // Remove this user from the list
821829 mUsers .remove (userHandle );
822- mRemovingUserIds .remove (userHandle );
830+
831+ // Have user ID linger for several seconds to let external storage VFS
832+ // cache entries expire. This must be greater than the 'entry_valid'
833+ // timeout used by the FUSE daemon.
834+ mHandler .postDelayed (new Runnable () {
835+ @ Override
836+ public void run () {
837+ synchronized (mPackagesLock ) {
838+ mRemovingUserIds .delete (userHandle );
839+ }
840+ }
841+ }, MINUTE_IN_MILLIS );
842+
823843 // Remove user file
824844 AtomicFile userFile = new AtomicFile (new File (mUsersDir , userHandle + ".xml" ));
825845 userFile .delete ();
@@ -906,14 +926,13 @@ public void userForeground(int userId) {
906926 */
907927 private int getNextAvailableIdLocked () {
908928 synchronized (mPackagesLock ) {
909- int i = mNextUserId ;
929+ int i = MIN_USER_ID ;
910930 while (i < Integer .MAX_VALUE ) {
911- if (mUsers .indexOfKey (i ) < 0 && !mRemovingUserIds .contains (i )) {
931+ if (mUsers .indexOfKey (i ) < 0 && !mRemovingUserIds .get (i )) {
912932 break ;
913933 }
914934 i ++;
915935 }
916- mNextUserId = i + 1 ;
917936 return i ;
918937 }
919938 }
@@ -938,7 +957,7 @@ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
938957 UserInfo user = mUsers .valueAt (i );
939958 if (user == null ) continue ;
940959 pw .print (" " ); pw .print (user ); pw .print (" serialNo=" ); pw .print (user .serialNumber );
941- if (mRemovingUserIds .contains (mUsers .keyAt (i ))) pw .print (" <removing> " );
960+ if (mRemovingUserIds .get (mUsers .keyAt (i ))) pw .print (" <removing> " );
942961 if (user .partial ) pw .print (" <partial>" );
943962 pw .println ();
944963 pw .print (" Created: " );
0 commit comments