summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmds/am/src/com/android/commands/am/Am.java17
-rw-r--r--core/java/android/app/ActivityManager.java6
-rw-r--r--core/java/android/app/ActivityManagerNative.java7
-rw-r--r--core/java/android/app/IActivityManager.java2
-rw-r--r--core/java/android/os/UserManager.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java2
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java4
-rw-r--r--services/core/java/com/android/server/am/UserController.java174
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java10
-rw-r--r--services/core/java/com/android/server/pm/UserRestrictionsUtils.java34
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java11
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);
}
}
}