diff options
11 files changed, 208 insertions, 72 deletions
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java index daf01ec06d0e..2ad63b53efc6 100644 --- a/cmds/am/src/com/android/commands/am/Am.java +++ b/cmds/am/src/com/android/commands/am/Am.java @@ -154,7 +154,7 @@ public class Am extends BaseCommand { " am switch-user <USER_ID>\n" + " am start-user <USER_ID>\n" + " am unlock-user <USER_ID> [TOKEN_HEX]\n" + - " am stop-user [-w] <USER_ID>\n" + + " am stop-user [-w] [-f] <USER_ID>\n" + " am stack start <DISPLAY_ID> <INTENT>\n" + " am stack movetask <TASK_ID> <STACK_ID> [true|false]\n" + " am stack resize <STACK_ID> <LEFT,TOP,RIGHT,BOTTOM>\n" + @@ -290,6 +290,7 @@ public class Am extends BaseCommand { "am stop-user: stop execution of USER_ID, not allowing it to run any\n" + " code until a later explicit start or switch to it.\n" + " -w: wait for stop-user to complete.\n" + + " -f: force stop even if there are related users that cannot be stopped.\n" + "\n" + "am stack start: start a new activity on <DISPLAY_ID> using <INTENT>.\n" + "\n" + @@ -1131,10 +1132,13 @@ public class Am extends BaseCommand { private void runStopUser() throws Exception { boolean wait = false; - String opt = null; + boolean force = false; + String opt; while ((opt = nextOption()) != null) { if ("-w".equals(opt)) { wait = true; + } else if ("-f".equals(opt)) { + force = true; } else { System.err.println("Error: unknown option: " + opt); return; @@ -1143,7 +1147,7 @@ public class Am extends BaseCommand { int user = Integer.parseInt(nextArgRequired()); StopUserCallback callback = wait ? new StopUserCallback() : null; - int res = mAm.stopUser(user, callback); + int res = mAm.stopUser(user, force, callback); if (res != ActivityManager.USER_OP_SUCCESS) { String txt = ""; switch (res) { @@ -1153,6 +1157,13 @@ public class Am extends BaseCommand { case ActivityManager.USER_OP_UNKNOWN_USER: txt = " (Unknown user " + user + ")"; break; + case ActivityManager.USER_OP_ERROR_IS_SYSTEM: + txt = " (System user cannot be stopped)"; + break; + case ActivityManager.USER_OP_ERROR_RELATED_USERS_CANNOT_STOP: + txt = " (Can't stop user " + user + + " - one of its related users can't be stopped)"; + break; } System.err.println("Switch failed: " + res + txt); } else if (callback != null) { diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index f1a7de81d5ac..f7d765c7c86f 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -265,6 +265,12 @@ public class ActivityManager { /** @hide User operation call: given user id is the current user, can't be stopped. */ public static final int USER_OP_IS_CURRENT = -2; + /** @hide User operation call: system user can't be stopped. */ + public static final int USER_OP_ERROR_IS_SYSTEM = -3; + + /** @hide User operation call: one of related users cannot be stopped. */ + public static final int USER_OP_ERROR_RELATED_USERS_CANNOT_STOP = -4; + /** @hide Process does not exist. */ public static final int PROCESS_STATE_NONEXISTENT = -1; diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index 19d9fc2a676e..c05d5e8d8295 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -1981,9 +1981,10 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM case STOP_USER_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); int userid = data.readInt(); + boolean force = data.readInt() != 0; IStopUserCallback callback = IStopUserCallback.Stub.asInterface( data.readStrongBinder()); - int result = stopUser(userid, callback); + int result = stopUser(userid, force, callback); reply.writeNoException(); reply.writeInt(result); return true; @@ -5287,11 +5288,13 @@ class ActivityManagerProxy implements IActivityManager return result; } - public int stopUser(int userid, IStopUserCallback callback) throws RemoteException { + public int stopUser(int userid, boolean force, IStopUserCallback callback) + throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); data.writeInt(userid); + data.writeInt(force ? 1 : 0); data.writeStrongInterface(callback); mRemote.transact(STOP_USER_TRANSACTION, data, reply, 0); reply.readException(); diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 09c6c0bd4d7c..38c795797bf8 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -391,7 +391,7 @@ public interface IActivityManager extends IInterface { public boolean switchUser(int userid) throws RemoteException; public boolean startUserInBackground(int userid) throws RemoteException; public boolean unlockUser(int userid, byte[] token) throws RemoteException; - public int stopUser(int userid, IStopUserCallback callback) throws RemoteException; + public int stopUser(int userid, boolean force, IStopUserCallback callback) throws RemoteException; public UserInfo getCurrentUser() throws RemoteException; public boolean isUserRunning(int userid, int flags) throws RemoteException; public int[] getRunningUserIds() throws RemoteException; diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index f26693c853fb..4723025c59e9 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -487,6 +487,19 @@ public class UserManager { public static final String DISALLOW_RECORD_AUDIO = "no_record_audio"; /** + * Specifies if a user is not allowed to run in the background and should be stopped during + * user switch. The default value is <code>false</code>. + * + * <p>This restriction can be set by device owners and profile owners. + * + * @see DevicePolicyManager#addUserRestriction(ComponentName, String) + * @see DevicePolicyManager#clearUserRestriction(ComponentName, String) + * @see #getUserRestrictions() + * @hide + */ + public static final String DISALLOW_RUN_IN_BACKGROUND = "no_run_in_background"; + + /** * Specifies if a user is not allowed to use the camera. * * @see DevicePolicyManager#addUserRestriction(ComponentName, String) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java index e00b8907a01a..b010761aecc7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java @@ -342,7 +342,7 @@ public class UserSwitcherController { private void stopUserId(int id) { try { - ActivityManagerNative.getDefault().stopUser(id, null); + ActivityManagerNative.getDefault().stopUser(id, /* force= */ false, null); } catch (RemoteException e) { Log.e(TAG, "Couldn't stop user.", e); } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index bbf8652887b3..7649f9316dde 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -20424,8 +20424,8 @@ public final class ActivityManagerService extends ActivityManagerNative } @Override - public int stopUser(final int userId, final IStopUserCallback callback) { - return mUserController.stopUser(userId, callback); + public int stopUser(final int userId, boolean force, final IStopUserCallback callback) { + return mUserController.stopUser(userId, force, callback); } void onUserRemovedLocked(int userId) { diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index 9c2914976bac..12bd7f97e0cb 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -18,6 +18,8 @@ package com.android.server.am; import static android.Manifest.permission.INTERACT_ACROSS_USERS; import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; +import static android.app.ActivityManager.USER_OP_ERROR_IS_SYSTEM; +import static android.app.ActivityManager.USER_OP_ERROR_RELATED_USERS_CANNOT_STOP; import static android.app.ActivityManager.USER_OP_IS_CURRENT; import static android.app.ActivityManager.USER_OP_SUCCESS; import static android.os.Process.SYSTEM_UID; @@ -34,6 +36,7 @@ import static com.android.server.am.ActivityManagerService.SYSTEM_USER_CURRENT_M import static com.android.server.am.ActivityManagerService.SYSTEM_USER_START_MSG; import static com.android.server.am.ActivityManagerService.USER_SWITCH_TIMEOUT_MSG; +import android.annotation.NonNull; import android.app.ActivityManager; import android.app.AppOpsManager; import android.app.Dialog; @@ -56,11 +59,11 @@ import android.os.Process; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.ServiceManager; -import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; import android.os.storage.IMountService; import android.os.storage.StorageManager; +import android.util.IntArray; import android.util.Pair; import android.util.Slog; import android.util.SparseArray; @@ -78,6 +81,8 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import libcore.util.EmptyArray; + /** * Helper class for {@link ActivityManagerService} responsible for multi-user functionality. */ @@ -152,39 +157,44 @@ final class UserController { finishUserBoot(uss); startProfilesLocked(); + stopRunningUsersLocked(MAX_RUNNING_USERS); + } + } - int num = mUserLru.size(); - int i = 0; - while (num > MAX_RUNNING_USERS && i < mUserLru.size()) { - Integer oldUserId = mUserLru.get(i); - UserState oldUss = mStartedUsers.get(oldUserId); - if (oldUss == null) { - // Shouldn't happen, but be sane if it does. - mUserLru.remove(i); - num--; - continue; - } - if (oldUss.mState == UserState.STATE_STOPPING - || oldUss.mState == UserState.STATE_SHUTDOWN) { - // This user is already stopping, doesn't count. + void stopRunningUsersLocked(int maxRunningUsers) { + int num = mUserLru.size(); + int i = 0; + while (num > maxRunningUsers && i < mUserLru.size()) { + Integer oldUserId = mUserLru.get(i); + UserState oldUss = mStartedUsers.get(oldUserId); + if (oldUss == null) { + // Shouldn't happen, but be sane if it does. + mUserLru.remove(i); + num--; + continue; + } + if (oldUss.mState == UserState.STATE_STOPPING + || oldUss.mState == UserState.STATE_SHUTDOWN) { + // This user is already stopping, doesn't count. + num--; + i++; + continue; + } + if (oldUserId == UserHandle.USER_SYSTEM || oldUserId == mCurrentUserId) { + // Owner/System user and current user can't be stopped. We count it as running + // when it is not a pure system user. + if (UserInfo.isSystemOnly(oldUserId)) { num--; - i++; - continue; } - if (oldUserId == UserHandle.USER_SYSTEM || oldUserId == mCurrentUserId) { - // Owner/System user and current user can't be stopped. We count it as running - // when it is not a pure system user. - if (UserInfo.isSystemOnly(oldUserId)) { - num--; - } - i++; - continue; - } - // This is a user to be stopped. - stopUserLocked(oldUserId, null); - num--; i++; + continue; } + // This is a user to be stopped. + if (stopUsersLocked(oldUserId, false, null) != USER_OP_SUCCESS) { + num--; + } + num--; + i++; } } @@ -205,7 +215,7 @@ final class UserController { } } - int stopUser(final int userId, final IStopUserCallback callback) { + int stopUser(final int userId, final boolean force, final IStopUserCallback callback) { if (mService.checkCallingPermission(INTERACT_ACROSS_USERS_FULL) != PackageManager.PERMISSION_GRANTED) { String msg = "Permission Denial: switchUser() from pid=" @@ -221,16 +231,44 @@ final class UserController { mService.enforceShellRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, userId); synchronized (mService) { - return stopUserLocked(userId, callback); + return stopUsersLocked(userId, force, callback); } } - private int stopUserLocked(final int userId, final IStopUserCallback callback) { - if (DEBUG_MU) Slog.i(TAG, "stopUserLocked userId=" + userId); - if (mCurrentUserId == userId && mTargetUserId == UserHandle.USER_NULL) { + /** + * Stops the user along with its related users. The method calls + * {@link #getUsersToStopLocked(int)} to determine the list of users that should be stopped. + */ + private int stopUsersLocked(final int userId, boolean force, final IStopUserCallback callback) { + if (userId == UserHandle.USER_SYSTEM) { + return USER_OP_ERROR_IS_SYSTEM; + } + if (isCurrentUserLocked(userId)) { return USER_OP_IS_CURRENT; } + int[] usersToStop = getUsersToStopLocked(userId); + // If one of related users is system or current, no related users should be stopped + for (int i = 0; i < usersToStop.length; i++) { + int relatedUserId = usersToStop[i]; + if ((UserHandle.USER_SYSTEM == relatedUserId) || isCurrentUserLocked(relatedUserId)) { + if (DEBUG_MU) Slog.i(TAG, "stopUsersLocked cannot stop related user " + + relatedUserId); + // We still need to stop the requested user if it's a force stop. + if (force) { + stopSingleUserLocked(userId, callback); + } + return USER_OP_ERROR_RELATED_USERS_CANNOT_STOP; + } + } + if (DEBUG_MU) Slog.i(TAG, "stopUsersLocked usersToStop=" + Arrays.toString(usersToStop)); + for (int userIdToStop : usersToStop) { + stopSingleUserLocked(userIdToStop, userIdToStop == userId ? callback : null); + } + return USER_OP_SUCCESS; + } + private void stopSingleUserLocked(final int userId, final IStopUserCallback callback) { + if (DEBUG_MU) Slog.i(TAG, "stopSingleUserLocked userId=" + userId); final UserState uss = mStartedUsers.get(userId); if (uss == null) { // User is not started, nothing to do... but we do need to @@ -246,7 +284,7 @@ final class UserController { } }); } - return USER_OP_SUCCESS; + return; } if (callback != null) { @@ -307,8 +345,6 @@ final class UserController { Binder.restoreCallingIdentity(ident); } } - - return USER_OP_SUCCESS; } void finishUserStop(UserState uss) { @@ -350,6 +386,36 @@ final class UserController { } } + /** + * Determines the list of users that should be stopped together with the specified + * {@code userId}. The returned list includes {@code userId}. + */ + private @NonNull int[] getUsersToStopLocked(int userId) { + int startedUsersSize = mStartedUsers.size(); + IntArray userIds = new IntArray(); + userIds.add(userId); + synchronized (mUserProfileGroupIdsSelfLocked) { + int userGroupId = mUserProfileGroupIdsSelfLocked.get(userId, + UserInfo.NO_PROFILE_GROUP_ID); + for (int i = 0; i < startedUsersSize; i++) { + UserState uss = mStartedUsers.valueAt(i); + int startedUserId = uss.mHandle.getIdentifier(); + // Skip unrelated users (profileGroupId mismatch) + int startedUserGroupId = mUserProfileGroupIdsSelfLocked.get(startedUserId, + UserInfo.NO_PROFILE_GROUP_ID); + boolean sameGroup = (userGroupId != UserInfo.NO_PROFILE_GROUP_ID) + && (userGroupId == startedUserGroupId); + // userId has already been added + boolean sameUserId = startedUserId == userId; + if (!sameGroup || sameUserId) { + continue; + } + userIds.add(startedUserId); + } + } + return userIds.toArray(); + } + private void forceStopUserLocked(int userId, String reason) { mService.forceStopPackageLocked(null, -1, false, false, true, false, false, userId, reason); @@ -362,7 +428,6 @@ final class UserController { null, false, false, MY_PID, SYSTEM_UID, UserHandle.USER_ALL); } - /** * Stops the guest user if it has gone to the background. */ @@ -380,7 +445,7 @@ final class UserController { UserInfo userInfo = getUserInfo(oldUserId); if (userInfo.isGuest()) { // This is a user to be stopped. - stopUserLocked(oldUserId, null); + stopUsersLocked(oldUserId, true, null); break; } } @@ -476,7 +541,7 @@ final class UserController { // If the user we are switching to is not currently started, then // we need to start it now. if (mStartedUsers.get(userId) == null) { - mStartedUsers.put(userId, new UserState(new UserHandle(userId))); + mStartedUsers.put(userId, new UserState(UserHandle.of(userId))); updateStartedUserArrayLocked(); needStart = true; } @@ -695,6 +760,24 @@ final class UserController { mUserSwitchObservers.finishBroadcast(); } + private void stopBackgroundUsersIfEnforced(int oldUserId) { + // Never stop system user + if (oldUserId == UserHandle.USER_SYSTEM) { + return; + } + // For now, only check for user restriction. Additional checks can be added here + boolean disallowRunInBg = hasUserRestriction(UserManager.DISALLOW_RUN_IN_BACKGROUND, + oldUserId); + if (!disallowRunInBg) { + return; + } + synchronized (mService) { + if (DEBUG_MU) Slog.i(TAG, "stopBackgroundUsersIfEnforced stopping " + oldUserId + + " and related users"); + stopUsersLocked(oldUserId, false, null); + } + } + void timeoutUserSwitch(UserState uss, int oldUserId, int newUserId) { synchronized (mService) { Slog.wtf(TAG, "User switch timeout: from " + oldUserId + " to " + newUserId); @@ -747,7 +830,7 @@ final class UserController { } void continueUserSwitch(UserState uss, int oldUserId, int newUserId) { - completeSwitchAndInitialize(uss, newUserId, false, true); + completeSwitchAndInitialize(uss, oldUserId, newUserId, false, true); } void onUserInitialized(UserState uss, boolean foreground, int oldUserId, int newUserId) { @@ -756,10 +839,10 @@ final class UserController { moveUserToForegroundLocked(uss, oldUserId, newUserId); } } - completeSwitchAndInitialize(uss, newUserId, true, false); + completeSwitchAndInitialize(uss, oldUserId, newUserId, true, false); } - void completeSwitchAndInitialize(UserState uss, int newUserId, + void completeSwitchAndInitialize(UserState uss, int oldUserId, int newUserId, boolean clearInitializing, boolean clearSwitching) { boolean unfrozen = false; synchronized (mService) { @@ -781,6 +864,7 @@ final class UserController { newUserId, 0)); } stopGuestUserIfBackground(); + stopBackgroundUsersIfEnforced(oldUserId); } void moveUserToForegroundLocked(UserState uss, int oldUserId, int newUserId) { @@ -1074,6 +1158,10 @@ final class UserController { return mCurrentUserId; } + private boolean isCurrentUserLocked(int userId) { + return mCurrentUserId == userId || mTargetUserId == userId; + } + int setTargetUserIdLocked(int targetUserId) { return mTargetUserId = targetUserId; } diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index da10a94215dc..b31d731e46b9 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -869,10 +869,8 @@ public class UserManagerService extends IUserManager.Stub { mHandler.post(new Runnable() { @Override public void run() { - synchronized (mRestrictionsLock) { - UserRestrictionsUtils.applyUserRestrictionsLR( - mContext, userId, newRestrictionsFinal, prevRestrictionsFinal); - } + UserRestrictionsUtils.applyUserRestrictions( + mContext, userId, newRestrictionsFinal, prevRestrictionsFinal); final UserRestrictionsListener[] listeners; synchronized (mUserRestrictionsListeners) { @@ -1811,8 +1809,8 @@ public class UserManagerService extends IUserManager.Stub { if (DBG) Slog.i(LOG_TAG, "Stopping user " + userHandle); int res; try { - res = ActivityManagerNative.getDefault().stopUser(userHandle, - new IStopUserCallback.Stub() { + res = ActivityManagerNative.getDefault().stopUser(userHandle, /* force= */ true, + new IStopUserCallback.Stub() { @Override public void userStopped(int userId) { finishRemoveUser(userId); diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java index 77abd3ee0790..816903ec4901 100644 --- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java +++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java @@ -22,11 +22,14 @@ import com.android.internal.util.Preconditions; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.ActivityManager; +import android.app.ActivityManagerNative; import android.content.ContentResolver; import android.content.Context; import android.net.Uri; import android.os.Binder; import android.os.Bundle; +import android.os.RemoteException; import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; @@ -84,7 +87,8 @@ public class UserRestrictionsUtils { UserManager.DISALLOW_SAFE_BOOT, UserManager.ALLOW_PARENT_PROFILE_APP_LINKING, UserManager.DISALLOW_RECORD_AUDIO, - UserManager.DISALLOW_CAMERA + UserManager.DISALLOW_CAMERA, + UserManager.DISALLOW_RUN_IN_BACKGROUND ); /** @@ -126,6 +130,7 @@ public class UserRestrictionsUtils { */ private static final Set<String> GLOBAL_RESTRICTIONS = Sets.newArraySet( UserManager.DISALLOW_ADJUST_VOLUME, + UserManager.DISALLOW_RUN_IN_BACKGROUND, UserManager.DISALLOW_UNMUTE_MICROPHONE ); @@ -263,34 +268,28 @@ public class UserRestrictionsUtils { * Takes a new use restriction set and the previous set, and apply the restrictions that have * changed. * - * <p>Note this method is called by {@link UserManagerService} while holding - * {@code mRestrictionLock}. Be aware when calling into other services, which could cause - * a deadlock. + * <p>Note this method is called by {@link UserManagerService} without holding any locks. */ - public static void applyUserRestrictionsLR(Context context, int userId, + public static void applyUserRestrictions(Context context, int userId, Bundle newRestrictions, Bundle prevRestrictions) { for (String key : USER_RESTRICTIONS) { final boolean newValue = newRestrictions.getBoolean(key); final boolean prevValue = prevRestrictions.getBoolean(key); if (newValue != prevValue) { - applyUserRestrictionLR(context, userId, key, newValue); + applyUserRestriction(context, userId, key, newValue); } } } - + /** * Apply each user restriction. * - * <p>Note this method is called by {@link UserManagerService} while holding - * {@code mRestrictionLock}. Be aware when calling into other services, which could cause - * a deadlock. - * * <p>See also {@link * com.android.providers.settings.SettingsProvider#isGlobalOrSecureSettingRestrictedForUser}, * which should be in sync with this method. */ - private static void applyUserRestrictionLR(Context context, int userId, String key, + private static void applyUserRestriction(Context context, int userId, String key, boolean newValue) { if (UserManagerService.DBG) { Log.d(TAG, "Applying user restriction: userId=" + userId @@ -365,6 +364,17 @@ public class UserRestrictionsUtils { userId); } break; + case UserManager.DISALLOW_RUN_IN_BACKGROUND: + if (newValue) { + int currentUser = ActivityManager.getCurrentUser(); + if (currentUser != userId && userId != UserHandle.USER_SYSTEM) { + try { + ActivityManagerNative.getDefault().stopUser(userId, false, null); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + } } } finally { Binder.restoreCallingIdentity(id); 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 66c7dbb16ede..b4bca3eb761c 100644 --- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java @@ -31,7 +31,8 @@ import java.util.List; /** Test {@link UserManager} functionality. */ public class UserManagerTest extends AndroidTestCase { - + private static final int REMOVE_CHECK_INTERVAL = 500; + private static final int REMOVE_TIMEOUT = 60 * 1000; private UserManager mUserManager = null; private final Object mUserLock = new Object(); private List<Integer> usersToRemove; @@ -227,10 +228,16 @@ public class UserManagerTest extends AndroidTestCase { private void removeUser(int userId) { synchronized (mUserLock) { mUserManager.removeUser(userId); + long time = System.currentTimeMillis(); while (mUserManager.getUserInfo(userId) != null) { try { - mUserLock.wait(500); + mUserLock.wait(REMOVE_CHECK_INTERVAL); } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + return; + } + if (System.currentTimeMillis() - time > REMOVE_TIMEOUT) { + fail("Timeout waiting for removeUser. userId = " + userId); } } } |