Skip to content

Commit 8074e98

Browse files
Amith YamasaniAndroid (Google) Code Review
authored andcommitted
Merge "Fix crashes when quickly adding and removing users" into jb-mr1-dev
2 parents 7707b05 + db6a14c commit 8074e98

File tree

6 files changed

+94
-24
lines changed

6 files changed

+94
-24
lines changed

core/java/android/content/Intent.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2430,7 +2430,8 @@ public static Intent createChooser(Intent target, CharSequence title) {
24302430
/**
24312431
* Broadcast sent to the system when a user is removed. Carries an extra EXTRA_USER_HANDLE that has
24322432
* the userHandle of the user. It is sent to all running users except the
2433-
* one that has been removed. You must hold
2433+
* one that has been removed. The user will not be completely removed until all receivers have
2434+
* handled the broadcast. You must hold
24342435
* {@link android.Manifest.permission#MANAGE_USERS} to receive this broadcast.
24352436
* @hide
24362437
*/

core/java/android/content/SyncManager.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,9 @@ public void updateRunningAccounts() {
265265
}
266266

267267
private void doDatabaseCleanup() {
268-
for (UserInfo user : mUserManager.getUsers()) {
268+
for (UserInfo user : mUserManager.getUsers(true)) {
269+
// Skip any partially created/removed users
270+
if (user.partial) continue;
269271
Account[] accountsForUser = AccountManagerService.getSingleton().getAccounts(user.id);
270272
mSyncStorageEngine.doDatabaseCleanup(accountsForUser, user.id);
271273
}

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2575,7 +2575,7 @@ ResolveInfo findPreferredActivity(Intent intent, String resolvedType,
25752575
@Override
25762576
public List<ResolveInfo> queryIntentActivities(Intent intent,
25772577
String resolvedType, int flags, int userId) {
2578-
if (!sUserManager.exists(userId)) return null;
2578+
if (!sUserManager.exists(userId)) return Collections.emptyList();
25792579
enforceCrossUserPermission(Binder.getCallingUid(), userId, false, "query intent activities");
25802580
ComponentName comp = intent.getComponent();
25812581
if (comp == null) {
@@ -2615,7 +2615,7 @@ public List<ResolveInfo> queryIntentActivities(Intent intent,
26152615
public List<ResolveInfo> queryIntentActivityOptions(ComponentName caller,
26162616
Intent[] specifics, String[] specificTypes, Intent intent,
26172617
String resolvedType, int flags, int userId) {
2618-
if (!sUserManager.exists(userId)) return null;
2618+
if (!sUserManager.exists(userId)) return Collections.emptyList();
26192619
enforceCrossUserPermission(Binder.getCallingUid(), userId, false,
26202620
"query intent activity options");
26212621
final String resultsAction = intent.getAction();
@@ -2787,7 +2787,7 @@ public List<ResolveInfo> queryIntentActivityOptions(ComponentName caller,
27872787
@Override
27882788
public List<ResolveInfo> queryIntentReceivers(Intent intent, String resolvedType, int flags,
27892789
int userId) {
2790-
if (!sUserManager.exists(userId)) return null;
2790+
if (!sUserManager.exists(userId)) return Collections.emptyList();
27912791
ComponentName comp = intent.getComponent();
27922792
if (comp == null) {
27932793
if (intent.getSelector() != null) {
@@ -2838,7 +2838,7 @@ public ResolveInfo resolveService(Intent intent, String resolvedType, int flags,
28382838
@Override
28392839
public List<ResolveInfo> queryIntentServices(Intent intent, String resolvedType, int flags,
28402840
int userId) {
2841-
if (!sUserManager.exists(userId)) return null;
2841+
if (!sUserManager.exists(userId)) return Collections.emptyList();
28422842
ComponentName comp = intent.getComponent();
28432843
if (comp == null) {
28442844
if (intent.getSelector() != null) {

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

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,11 @@
1919
import com.android.internal.util.ArrayUtils;
2020
import com.android.internal.util.FastXmlSerializer;
2121

22+
import android.app.Activity;
2223
import android.app.ActivityManager;
2324
import android.app.ActivityManagerNative;
2425
import android.app.IStopUserCallback;
26+
import android.content.BroadcastReceiver;
2527
import android.content.Context;
2628
import android.content.Intent;
2729
import android.content.pm.PackageManager;
@@ -736,21 +738,38 @@ public void userStopAborted(int userId) {
736738
return res == ActivityManager.USER_OP_SUCCESS;
737739
}
738740

739-
void finishRemoveUser(int userHandle) {
741+
void finishRemoveUser(final int userHandle) {
740742
if (DBG) Slog.i(LOG_TAG, "finishRemoveUser " + userHandle);
741-
synchronized (mInstallLock) {
742-
synchronized (mPackagesLock) {
743-
removeUserStateLocked(userHandle);
744-
}
745-
}
746-
if (DBG) Slog.i(LOG_TAG, "Removed user " + userHandle + ", sending broadcast");
747-
// Let other services shutdown any activity
743+
// Let other services shutdown any activity and clean up their state before completely
744+
// wiping the user's system directory and removing from the user list
748745
long ident = Binder.clearCallingIdentity();
749746
try {
750747
Intent addedIntent = new Intent(Intent.ACTION_USER_REMOVED);
751748
addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userHandle);
752-
mContext.sendBroadcastAsUser(addedIntent, UserHandle.ALL,
753-
android.Manifest.permission.MANAGE_USERS);
749+
mContext.sendOrderedBroadcastAsUser(addedIntent, UserHandle.ALL,
750+
android.Manifest.permission.MANAGE_USERS,
751+
752+
new BroadcastReceiver() {
753+
@Override
754+
public void onReceive(Context context, Intent intent) {
755+
if (DBG) {
756+
Slog.i(LOG_TAG,
757+
"USER_REMOVED broadcast sent, cleaning up user data "
758+
+ userHandle);
759+
}
760+
new Thread() {
761+
public void run() {
762+
synchronized (mInstallLock) {
763+
synchronized (mPackagesLock) {
764+
removeUserStateLocked(userHandle);
765+
}
766+
}
767+
}
768+
}.start();
769+
}
770+
},
771+
772+
null, Activity.RESULT_OK, null, null);
754773
} finally {
755774
Binder.restoreCallingIdentity(ident);
756775
}

services/tests/servicestests/AndroidManifest.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
<uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
3535
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
3636
<uses-permission android:name="android.permission.MANAGE_USERS" />
37-
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
37+
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
3838

3939
<application>
4040
<uses-library android:name="android.test.runner" />

services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java

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

1717
package com.android.server.pm;
1818

19+
import android.content.BroadcastReceiver;
1920
import android.content.Context;
21+
import android.content.Intent;
22+
import android.content.IntentFilter;
2023
import android.content.pm.UserInfo;
2124
import android.os.Debug;
2225
import android.os.Environment;
2326
import android.os.UserManager;
2427
import android.test.AndroidTestCase;
2528

29+
import java.util.ArrayList;
2630
import java.util.List;
2731

2832
/** Test {@link UserManager} functionality. */
2933
public class UserManagerTest extends AndroidTestCase {
3034

3135
UserManager mUserManager = null;
36+
Object mUserLock = new Object();
3237

3338
@Override
3439
public void setUp() throws Exception {
3540
mUserManager = (UserManager) getContext().getSystemService(Context.USER_SERVICE);
41+
IntentFilter filter = new IntentFilter(Intent.ACTION_USER_REMOVED);
42+
getContext().registerReceiver(new BroadcastReceiver() {
43+
@Override
44+
public void onReceive(Context context, Intent intent) {
45+
synchronized (mUserLock) {
46+
mUserLock.notifyAll();
47+
}
48+
}
49+
}, filter);
3650
}
3751

3852
public void testHasPrimary() throws Exception {
@@ -54,7 +68,7 @@ public void testAddUser() throws Exception {
5468
}
5569
}
5670
assertTrue(found);
57-
mUserManager.removeUser(userInfo.id);
71+
removeUser(userInfo.id);
5872
}
5973

6074
public void testAdd2Users() throws Exception {
@@ -67,14 +81,13 @@ public void testAdd2Users() throws Exception {
6781
assertTrue(findUser(0));
6882
assertTrue(findUser(user1.id));
6983
assertTrue(findUser(user2.id));
70-
mUserManager.removeUser(user1.id);
71-
mUserManager.removeUser(user2.id);
84+
removeUser(user1.id);
85+
removeUser(user2.id);
7286
}
7387

7488
public void testRemoveUser() throws Exception {
7589
UserInfo userInfo = mUserManager.createUser("Guest 1", UserInfo.FLAG_GUEST);
76-
77-
mUserManager.removeUser(userInfo.id);
90+
removeUser(userInfo.id);
7891

7992
assertFalse(findUser(userInfo.id));
8093
}
@@ -95,12 +108,47 @@ public void testSerialNumber() {
95108
int serialNumber1 = user1.serialNumber;
96109
assertEquals(serialNumber1, mUserManager.getUserSerialNumber(user1.id));
97110
assertEquals(user1.id, mUserManager.getUserHandle(serialNumber1));
98-
mUserManager.removeUser(user1.id);
111+
removeUser(user1.id);
99112
UserInfo user2 = mUserManager.createUser("User 2", UserInfo.FLAG_RESTRICTED);
100113
int serialNumber2 = user2.serialNumber;
101114
assertFalse(serialNumber1 == serialNumber2);
102115
assertEquals(serialNumber2, mUserManager.getUserSerialNumber(user2.id));
103116
assertEquals(user2.id, mUserManager.getUserHandle(serialNumber2));
104-
mUserManager.removeUser(user2.id);
117+
removeUser(user2.id);
118+
}
119+
120+
public void testMaxUsers() {
121+
int N = UserManager.getMaxSupportedUsers();
122+
int count = mUserManager.getUsers().size();
123+
List<UserInfo> created = new ArrayList<UserInfo>();
124+
// Create as many users as permitted and make sure creation passes
125+
while (count < N) {
126+
UserInfo ui = mUserManager.createUser("User " + count, 0);
127+
assertNotNull(ui);
128+
created.add(ui);
129+
count++;
130+
}
131+
// Try to create one more user and make sure it fails
132+
UserInfo extra = null;
133+
assertNull(extra = mUserManager.createUser("One more", 0));
134+
if (extra != null) {
135+
removeUser(extra.id);
136+
}
137+
while (!created.isEmpty()) {
138+
UserInfo user = created.remove(0);
139+
removeUser(user.id);
140+
}
141+
}
142+
143+
private void removeUser(int userId) {
144+
synchronized (mUserLock) {
145+
mUserManager.removeUser(userId);
146+
while (mUserManager.getUserInfo(userId) != null) {
147+
try {
148+
mUserLock.wait(1000);
149+
} catch (InterruptedException ie) {
150+
}
151+
}
152+
}
105153
}
106154
}

0 commit comments

Comments
 (0)