diff options
author | Amith Yamasani <yamasani@google.com> | 2012-10-21 07:50:55 -0700 |
---|---|---|
committer | Android Git Automerger <android-git-automerger@android.com> | 2012-10-21 07:50:55 -0700 |
commit | 283d1409ddadd35cd41b5496659d3a24dc365fb0 (patch) | |
tree | 94875c181ba360a1960078173347726eb78e81b1 | |
parent | 27b94b3eac1bec47d9a5141e44eb093c290e4ce8 (diff) | |
parent | f624d72cb0fe40fd5748d4f0d1a93bf267aa3ed8 (diff) | |
download | frameworks_base-283d1409ddadd35cd41b5496659d3a24dc365fb0.zip frameworks_base-283d1409ddadd35cd41b5496659d3a24dc365fb0.tar.gz frameworks_base-283d1409ddadd35cd41b5496659d3a24dc365fb0.tar.bz2 |
am f624d72c: am 8074e98b: Merge "Fix crashes when quickly adding and removing users" into jb-mr1-dev
* commit 'f624d72cb0fe40fd5748d4f0d1a93bf267aa3ed8':
Fix crashes when quickly adding and removing users
6 files changed, 94 insertions, 24 deletions
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 97d299a..cf0603e 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -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 * 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. * @hide */ diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java index bb0c686..05bab9c 100644 --- a/core/java/android/content/SyncManager.java +++ b/core/java/android/content/SyncManager.java @@ -265,7 +265,9 @@ public class SyncManager { } 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); mSyncStorageEngine.doDatabaseCleanup(accountsForUser, user.id); } diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java index 977437b..4c6f589 100644 --- a/services/java/com/android/server/pm/PackageManagerService.java +++ b/services/java/com/android/server/pm/PackageManagerService.java @@ -2575,7 +2575,7 @@ public class PackageManagerService extends IPackageManager.Stub { @Override public List<ResolveInfo> queryIntentActivities(Intent intent, 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"); ComponentName comp = intent.getComponent(); if (comp == null) { @@ -2615,7 +2615,7 @@ public class PackageManagerService extends IPackageManager.Stub { public List<ResolveInfo> queryIntentActivityOptions(ComponentName caller, Intent[] specifics, String[] specificTypes, Intent intent, 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 activity options"); final String resultsAction = intent.getAction(); @@ -2787,7 +2787,7 @@ public class PackageManagerService extends IPackageManager.Stub { @Override public List<ResolveInfo> queryIntentReceivers(Intent intent, String resolvedType, int flags, int userId) { - if (!sUserManager.exists(userId)) return null; + if (!sUserManager.exists(userId)) return Collections.emptyList(); ComponentName comp = intent.getComponent(); if (comp == null) { if (intent.getSelector() != null) { @@ -2838,7 +2838,7 @@ public class PackageManagerService extends IPackageManager.Stub { @Override public List<ResolveInfo> queryIntentServices(Intent intent, String resolvedType, int flags, int userId) { - if (!sUserManager.exists(userId)) return null; + if (!sUserManager.exists(userId)) return Collections.emptyList(); ComponentName comp = intent.getComponent(); if (comp == null) { if (intent.getSelector() != null) { diff --git a/services/java/com/android/server/pm/UserManagerService.java b/services/java/com/android/server/pm/UserManagerService.java index 072dd33..fb93d05 100644 --- a/services/java/com/android/server/pm/UserManagerService.java +++ b/services/java/com/android/server/pm/UserManagerService.java @@ -19,9 +19,11 @@ package com.android.server.pm; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastXmlSerializer; +import android.app.Activity; import android.app.ActivityManager; import android.app.ActivityManagerNative; import android.app.IStopUserCallback; +import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; @@ -736,21 +738,38 @@ public class UserManagerService extends IUserManager.Stub { return res == ActivityManager.USER_OP_SUCCESS; } - void finishRemoveUser(int userHandle) { + void finishRemoveUser(final int userHandle) { if (DBG) Slog.i(LOG_TAG, "finishRemoveUser " + userHandle); - synchronized (mInstallLock) { - synchronized (mPackagesLock) { - removeUserStateLocked(userHandle); - } - } - if (DBG) Slog.i(LOG_TAG, "Removed user " + userHandle + ", sending broadcast"); - // Let other services shutdown any activity + // Let other services shutdown any activity and clean up their state before completely + // wiping the user's system directory and removing from the user list long ident = Binder.clearCallingIdentity(); try { Intent addedIntent = new Intent(Intent.ACTION_USER_REMOVED); addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userHandle); - mContext.sendBroadcastAsUser(addedIntent, UserHandle.ALL, - android.Manifest.permission.MANAGE_USERS); + mContext.sendOrderedBroadcastAsUser(addedIntent, UserHandle.ALL, + 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 { Binder.restoreCallingIdentity(ident); } diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml index 8aeb2af..7848b1d 100644 --- a/services/tests/servicestests/AndroidManifest.xml +++ b/services/tests/servicestests/AndroidManifest.xml @@ -34,7 +34,7 @@ <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <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> <uses-library android:name="android.test.runner" /> diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java index 59a86c2..1758d93 100644 --- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java @@ -16,23 +16,37 @@ package com.android.server.pm; +import android.content.BroadcastReceiver; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.content.pm.UserInfo; import android.os.Debug; import android.os.Environment; import android.os.UserManager; import android.test.AndroidTestCase; +import java.util.ArrayList; import java.util.List; /** Test {@link UserManager} functionality. */ public class UserManagerTest extends AndroidTestCase { UserManager mUserManager = null; + Object mUserLock = new Object(); @Override public void setUp() throws Exception { 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 { @@ -54,7 +68,7 @@ public class UserManagerTest extends AndroidTestCase { } } assertTrue(found); - mUserManager.removeUser(userInfo.id); + removeUser(userInfo.id); } public void testAdd2Users() throws Exception { @@ -67,14 +81,13 @@ public class UserManagerTest extends AndroidTestCase { assertTrue(findUser(0)); assertTrue(findUser(user1.id)); assertTrue(findUser(user2.id)); - mUserManager.removeUser(user1.id); - mUserManager.removeUser(user2.id); + removeUser(user1.id); + removeUser(user2.id); } public void testRemoveUser() throws Exception { UserInfo userInfo = mUserManager.createUser("Guest 1", UserInfo.FLAG_GUEST); - - mUserManager.removeUser(userInfo.id); + removeUser(userInfo.id); assertFalse(findUser(userInfo.id)); } @@ -95,12 +108,47 @@ public class UserManagerTest extends AndroidTestCase { int serialNumber1 = user1.serialNumber; assertEquals(serialNumber1, mUserManager.getUserSerialNumber(user1.id)); assertEquals(user1.id, mUserManager.getUserHandle(serialNumber1)); - mUserManager.removeUser(user1.id); + removeUser(user1.id); UserInfo user2 = mUserManager.createUser("User 2", UserInfo.FLAG_RESTRICTED); int serialNumber2 = user2.serialNumber; assertFalse(serialNumber1 == serialNumber2); assertEquals(serialNumber2, mUserManager.getUserSerialNumber(user2.id)); 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) { + } + } + } } } |