am 8074e98b: Merge "Fix crashes when quickly adding and removing users" into jb-mr1-dev

* commit '8074e98b20ac04fdc032a1ed00dd6763b7e04147':
  Fix crashes when quickly adding and removing users
This commit is contained in:
Amith Yamasani
2012-10-21 07:48:02 -07:00
committed by Android Git Automerger
6 changed files with 94 additions and 24 deletions

View File

@ -2430,7 +2430,8 @@ public class Intent implements Parcelable, Cloneable {
/** /**
* Broadcast sent to the system when a user is removed. Carries an extra EXTRA_USER_HANDLE that has * Broadcast sent to the system when a user is removed. Carries an extra EXTRA_USER_HANDLE that has
* the userHandle of the user. It is sent to all running users except the * the userHandle of the user. It is sent to all running users except the
* one that has been removed. You must hold * one that has been removed. The user will not be completely removed until all receivers have
* handled the broadcast. You must hold
* {@link android.Manifest.permission#MANAGE_USERS} to receive this broadcast. * {@link android.Manifest.permission#MANAGE_USERS} to receive this broadcast.
* @hide * @hide
*/ */

View File

@ -265,7 +265,9 @@ public class SyncManager {
} }
private void doDatabaseCleanup() { private void doDatabaseCleanup() {
for (UserInfo user : mUserManager.getUsers()) { for (UserInfo user : mUserManager.getUsers(true)) {
// Skip any partially created/removed users
if (user.partial) continue;
Account[] accountsForUser = AccountManagerService.getSingleton().getAccounts(user.id); Account[] accountsForUser = AccountManagerService.getSingleton().getAccounts(user.id);
mSyncStorageEngine.doDatabaseCleanup(accountsForUser, user.id); mSyncStorageEngine.doDatabaseCleanup(accountsForUser, user.id);
} }

View File

@ -2575,7 +2575,7 @@ public class PackageManagerService extends IPackageManager.Stub {
@Override @Override
public List<ResolveInfo> queryIntentActivities(Intent intent, public List<ResolveInfo> queryIntentActivities(Intent intent,
String resolvedType, int flags, int userId) { String resolvedType, int flags, int userId) {
if (!sUserManager.exists(userId)) return null; if (!sUserManager.exists(userId)) return Collections.emptyList();
enforceCrossUserPermission(Binder.getCallingUid(), userId, false, "query intent activities"); enforceCrossUserPermission(Binder.getCallingUid(), userId, false, "query intent activities");
ComponentName comp = intent.getComponent(); ComponentName comp = intent.getComponent();
if (comp == null) { if (comp == null) {
@ -2615,7 +2615,7 @@ public class PackageManagerService extends IPackageManager.Stub {
public List<ResolveInfo> queryIntentActivityOptions(ComponentName caller, public List<ResolveInfo> queryIntentActivityOptions(ComponentName caller,
Intent[] specifics, String[] specificTypes, Intent intent, Intent[] specifics, String[] specificTypes, Intent intent,
String resolvedType, int flags, int userId) { String resolvedType, int flags, int userId) {
if (!sUserManager.exists(userId)) return null; if (!sUserManager.exists(userId)) return Collections.emptyList();
enforceCrossUserPermission(Binder.getCallingUid(), userId, false, enforceCrossUserPermission(Binder.getCallingUid(), userId, false,
"query intent activity options"); "query intent activity options");
final String resultsAction = intent.getAction(); final String resultsAction = intent.getAction();
@ -2787,7 +2787,7 @@ public class PackageManagerService extends IPackageManager.Stub {
@Override @Override
public List<ResolveInfo> queryIntentReceivers(Intent intent, String resolvedType, int flags, public List<ResolveInfo> queryIntentReceivers(Intent intent, String resolvedType, int flags,
int userId) { int userId) {
if (!sUserManager.exists(userId)) return null; if (!sUserManager.exists(userId)) return Collections.emptyList();
ComponentName comp = intent.getComponent(); ComponentName comp = intent.getComponent();
if (comp == null) { if (comp == null) {
if (intent.getSelector() != null) { if (intent.getSelector() != null) {
@ -2838,7 +2838,7 @@ public class PackageManagerService extends IPackageManager.Stub {
@Override @Override
public List<ResolveInfo> queryIntentServices(Intent intent, String resolvedType, int flags, public List<ResolveInfo> queryIntentServices(Intent intent, String resolvedType, int flags,
int userId) { int userId) {
if (!sUserManager.exists(userId)) return null; if (!sUserManager.exists(userId)) return Collections.emptyList();
ComponentName comp = intent.getComponent(); ComponentName comp = intent.getComponent();
if (comp == null) { if (comp == null) {
if (intent.getSelector() != null) { if (intent.getSelector() != null) {

View File

@ -19,9 +19,11 @@ package com.android.server.pm;
import com.android.internal.util.ArrayUtils; import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.FastXmlSerializer;
import android.app.Activity;
import android.app.ActivityManager; import android.app.ActivityManager;
import android.app.ActivityManagerNative; import android.app.ActivityManagerNative;
import android.app.IStopUserCallback; import android.app.IStopUserCallback;
import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
@ -736,21 +738,38 @@ public class UserManagerService extends IUserManager.Stub {
return res == ActivityManager.USER_OP_SUCCESS; return res == ActivityManager.USER_OP_SUCCESS;
} }
void finishRemoveUser(int userHandle) { void finishRemoveUser(final int userHandle) {
if (DBG) Slog.i(LOG_TAG, "finishRemoveUser " + userHandle); if (DBG) Slog.i(LOG_TAG, "finishRemoveUser " + userHandle);
synchronized (mInstallLock) { // Let other services shutdown any activity and clean up their state before completely
synchronized (mPackagesLock) { // wiping the user's system directory and removing from the user list
removeUserStateLocked(userHandle);
}
}
if (DBG) Slog.i(LOG_TAG, "Removed user " + userHandle + ", sending broadcast");
// Let other services shutdown any activity
long ident = Binder.clearCallingIdentity(); long ident = Binder.clearCallingIdentity();
try { try {
Intent addedIntent = new Intent(Intent.ACTION_USER_REMOVED); Intent addedIntent = new Intent(Intent.ACTION_USER_REMOVED);
addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userHandle); addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userHandle);
mContext.sendBroadcastAsUser(addedIntent, UserHandle.ALL, mContext.sendOrderedBroadcastAsUser(addedIntent, UserHandle.ALL,
android.Manifest.permission.MANAGE_USERS); android.Manifest.permission.MANAGE_USERS,
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (DBG) {
Slog.i(LOG_TAG,
"USER_REMOVED broadcast sent, cleaning up user data "
+ userHandle);
}
new Thread() {
public void run() {
synchronized (mInstallLock) {
synchronized (mPackagesLock) {
removeUserStateLocked(userHandle);
}
}
}
}.start();
}
},
null, Activity.RESULT_OK, null, null);
} finally { } finally {
Binder.restoreCallingIdentity(ident); Binder.restoreCallingIdentity(ident);
} }

View File

@ -34,7 +34,7 @@
<uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" /> <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.MANAGE_USERS" /> <uses-permission android:name="android.permission.MANAGE_USERS" />
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" /> <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
<application> <application>
<uses-library android:name="android.test.runner" /> <uses-library android:name="android.test.runner" />

View File

@ -16,23 +16,37 @@
package com.android.server.pm; package com.android.server.pm;
import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.UserInfo; import android.content.pm.UserInfo;
import android.os.Debug; import android.os.Debug;
import android.os.Environment; import android.os.Environment;
import android.os.UserManager; import android.os.UserManager;
import android.test.AndroidTestCase; import android.test.AndroidTestCase;
import java.util.ArrayList;
import java.util.List; import java.util.List;
/** Test {@link UserManager} functionality. */ /** Test {@link UserManager} functionality. */
public class UserManagerTest extends AndroidTestCase { public class UserManagerTest extends AndroidTestCase {
UserManager mUserManager = null; UserManager mUserManager = null;
Object mUserLock = new Object();
@Override @Override
public void setUp() throws Exception { public void setUp() throws Exception {
mUserManager = (UserManager) getContext().getSystemService(Context.USER_SERVICE); mUserManager = (UserManager) getContext().getSystemService(Context.USER_SERVICE);
IntentFilter filter = new IntentFilter(Intent.ACTION_USER_REMOVED);
getContext().registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
synchronized (mUserLock) {
mUserLock.notifyAll();
}
}
}, filter);
} }
public void testHasPrimary() throws Exception { public void testHasPrimary() throws Exception {
@ -54,7 +68,7 @@ public class UserManagerTest extends AndroidTestCase {
} }
} }
assertTrue(found); assertTrue(found);
mUserManager.removeUser(userInfo.id); removeUser(userInfo.id);
} }
public void testAdd2Users() throws Exception { public void testAdd2Users() throws Exception {
@ -67,14 +81,13 @@ public class UserManagerTest extends AndroidTestCase {
assertTrue(findUser(0)); assertTrue(findUser(0));
assertTrue(findUser(user1.id)); assertTrue(findUser(user1.id));
assertTrue(findUser(user2.id)); assertTrue(findUser(user2.id));
mUserManager.removeUser(user1.id); removeUser(user1.id);
mUserManager.removeUser(user2.id); removeUser(user2.id);
} }
public void testRemoveUser() throws Exception { public void testRemoveUser() throws Exception {
UserInfo userInfo = mUserManager.createUser("Guest 1", UserInfo.FLAG_GUEST); UserInfo userInfo = mUserManager.createUser("Guest 1", UserInfo.FLAG_GUEST);
removeUser(userInfo.id);
mUserManager.removeUser(userInfo.id);
assertFalse(findUser(userInfo.id)); assertFalse(findUser(userInfo.id));
} }
@ -95,12 +108,47 @@ public class UserManagerTest extends AndroidTestCase {
int serialNumber1 = user1.serialNumber; int serialNumber1 = user1.serialNumber;
assertEquals(serialNumber1, mUserManager.getUserSerialNumber(user1.id)); assertEquals(serialNumber1, mUserManager.getUserSerialNumber(user1.id));
assertEquals(user1.id, mUserManager.getUserHandle(serialNumber1)); assertEquals(user1.id, mUserManager.getUserHandle(serialNumber1));
mUserManager.removeUser(user1.id); removeUser(user1.id);
UserInfo user2 = mUserManager.createUser("User 2", UserInfo.FLAG_RESTRICTED); UserInfo user2 = mUserManager.createUser("User 2", UserInfo.FLAG_RESTRICTED);
int serialNumber2 = user2.serialNumber; int serialNumber2 = user2.serialNumber;
assertFalse(serialNumber1 == serialNumber2); assertFalse(serialNumber1 == serialNumber2);
assertEquals(serialNumber2, mUserManager.getUserSerialNumber(user2.id)); assertEquals(serialNumber2, mUserManager.getUserSerialNumber(user2.id));
assertEquals(user2.id, mUserManager.getUserHandle(serialNumber2)); assertEquals(user2.id, mUserManager.getUserHandle(serialNumber2));
mUserManager.removeUser(user2.id); removeUser(user2.id);
}
public void testMaxUsers() {
int N = UserManager.getMaxSupportedUsers();
int count = mUserManager.getUsers().size();
List<UserInfo> created = new ArrayList<UserInfo>();
// Create as many users as permitted and make sure creation passes
while (count < N) {
UserInfo ui = mUserManager.createUser("User " + count, 0);
assertNotNull(ui);
created.add(ui);
count++;
}
// Try to create one more user and make sure it fails
UserInfo extra = null;
assertNull(extra = mUserManager.createUser("One more", 0));
if (extra != null) {
removeUser(extra.id);
}
while (!created.isEmpty()) {
UserInfo user = created.remove(0);
removeUser(user.id);
}
}
private void removeUser(int userId) {
synchronized (mUserLock) {
mUserManager.removeUser(userId);
while (mUserManager.getUserInfo(userId) != null) {
try {
mUserLock.wait(1000);
} catch (InterruptedException ie) {
}
}
}
} }
} }