Skip to content

Commit ffe0cb4

Browse files
committed
Avoid recycling recently removed user IDs.
Currently, installd doesn't correctly evict VFS cache entries for FUSE emulated external storage. This means zygote processes have an inconsistent view of the FUSE daemon when the system rapidly recycles user IDs. To work around this, only consider recycling a user ID after its VFS cache entries have expired. The emulated storage FUSE daemon currently uses a 'entry_valid' timeout of 10 seconds. Bug: 7407902 Change-Id: Id80cbdd2215d8456467fb31e4c209ca12a505e16
1 parent f345680 commit ffe0cb4

File tree

1 file changed

+42
-23
lines changed

1 file changed

+42
-23
lines changed

services/java/com/android/server/pm/UserManagerService.java

Lines changed: 42 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,7 @@
1616

1717
package 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

2221
import android.app.Activity;
2322
import android.app.ActivityManager;
@@ -26,14 +25,14 @@
2625
import android.content.BroadcastReceiver;
2726
import android.content.Context;
2827
import android.content.Intent;
29-
import android.content.SharedPreferences;
3028
import android.content.pm.PackageManager;
3129
import android.content.pm.UserInfo;
3230
import android.graphics.Bitmap;
3331
import android.graphics.BitmapFactory;
3432
import android.os.Binder;
3533
import android.os.Environment;
3634
import android.os.FileUtils;
35+
import android.os.Handler;
3736
import android.os.IUserManager;
3837
import android.os.Process;
3938
import android.os.RemoteException;
@@ -42,9 +41,17 @@
4241
import android.util.AtomicFile;
4342
import android.util.Slog;
4443
import android.util.SparseArray;
44+
import android.util.SparseBooleanArray;
4545
import android.util.TimeUtils;
4646
import 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+
4855
import java.io.BufferedOutputStream;
4956
import java.io.File;
5057
import java.io.FileDescriptor;
@@ -54,13 +61,8 @@
5461
import java.io.IOException;
5562
import java.io.PrintWriter;
5663
import java.util.ArrayList;
57-
import java.util.HashSet;
5864
import java.util.List;
5965

60-
import org.xmlpull.v1.XmlPullParser;
61-
import org.xmlpull.v1.XmlPullParserException;
62-
import org.xmlpull.v1.XmlSerializer;
63-
6466
public 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

Comments
 (0)