Introduced UserController for multi-user functionality
The new helper class encapsulates user life-cycle management, provided by
ActivityManagerService.
Bug: 24745840
Change-Id: I8ebfa38febc4090390d1c45a9fc47398e52693ae
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 465118c2..df6b1d6 100755
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -355,7 +355,8 @@
final ServiceMap smap = getServiceMap(r.userId);
boolean addToStarting = false;
- if (!callerFg && r.app == null && mAm.mStartedUsers.get(r.userId) != null) {
+ if (!callerFg && r.app == null
+ && mAm.mUserController.hasStartedUserState(r.userId)) {
ProcessRecord proc = mAm.getProcessRecordLocked(r.processName, r.appInfo.uid, false);
if (proc == null || proc.curProcState > ActivityManager.PROCESS_STATE_RECEIVER) {
// If this is not coming from a foreground caller, then we may want
@@ -1401,7 +1402,7 @@
// Make sure that the user who owns this service is started. If not,
// we don't want to allow it to run.
- if (mAm.mStartedUsers.get(r.userId) == null) {
+ if (!mAm.mUserController.hasStartedUserState(r.userId)) {
String msg = "Unable to launch app "
+ r.appInfo.packageName + "/"
+ r.appInfo.uid + " for service "
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index bb1b6b8..c728b39 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -79,7 +79,6 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.DebugUtils;
-import android.util.SparseIntArray;
import android.view.Display;
import com.android.internal.R;
@@ -205,7 +204,6 @@
import android.os.IBinder;
import android.os.IPermissionController;
import android.os.IProcessInfoService;
-import android.os.IRemoteCallback;
import android.os.IUserManager;
import android.os.Looper;
import android.os.Message;
@@ -370,17 +368,10 @@
// How long we wait until we timeout on key dispatching during instrumentation.
static final int INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT = 60*1000;
- // Amount of time we wait for observers to handle a user switch before
- // giving up on them and unfreezing the screen.
- static final int USER_SWITCH_TIMEOUT = 2*1000;
-
// This is the amount of time an app needs to be running a foreground service before
// we will consider it to be doing interaction for usage stats.
static final int SERVICE_USAGE_INTERACTION_TIME = 30*60*1000;
- // Maximum number of users we allow to be running at a time.
- static final int MAX_RUNNING_USERS = 3;
-
// How long to wait in getAssistContextExtras for the activity and foreground services
// to respond with the result.
static final int PENDING_ASSIST_EXTRAS_TIMEOUT = 500;
@@ -505,6 +496,8 @@
*/
String mDeviceOwnerName;
+ final UserController mUserController;
+
public class PendingAssistExtras extends Binder implements Runnable {
public final ActivityRecord activity;
public final Bundle extras;
@@ -707,32 +700,6 @@
final SparseArray<UidRecord> mActiveUids = new SparseArray<>();
/**
- * Which users have been started, so are allowed to run code.
- */
- final SparseArray<UserState> mStartedUsers = new SparseArray<>();
-
- /**
- * LRU list of history of current users. Most recently current is at the end.
- */
- final ArrayList<Integer> mUserLru = new ArrayList<Integer>();
-
- /**
- * Constant array of the users that are currently started.
- */
- int[] mStartedUserArray = new int[] { 0 };
-
- /**
- * Registered observers of the user switching mechanics.
- */
- final RemoteCallbackList<IUserSwitchObserver> mUserSwitchObservers
- = new RemoteCallbackList<IUserSwitchObserver>();
-
- /**
- * Currently active user switch.
- */
- Object mCurUserSwitchCallback;
-
- /**
* Packages that the user has asked to have run in screen size
* compatibility mode instead of filling the screen.
*/
@@ -1300,19 +1267,6 @@
final ActivityThread mSystemThread;
- // Holds the current foreground user's id
- int mCurrentUserId = 0;
- // Holds the target user's id during a user switch
- int mTargetUserId = UserHandle.USER_NULL;
- // If there are multiple profiles for the current user, their ids are here
- // Currently only the primary user can have managed profiles
- int[] mCurrentProfileIds = new int[] {}; // Accessed by ActivityStack
-
- /**
- * Mapping from each known user ID to the profile group ID it is associated with.
- */
- SparseIntArray mUserProfileGroupIdsSelfLocked = new SparseIntArray();
-
private UserManagerService mUserManager;
private final class AppDeathRecipient implements IBinder.DeathRecipient {
@@ -1442,7 +1396,7 @@
boolean isBackground = (UserHandle.getAppId(proc.uid)
>= Process.FIRST_APPLICATION_UID
&& proc.pid != MY_PID);
- for (int userId : mCurrentProfileIds) {
+ for (int userId : mUserController.mCurrentProfileIds) {
isBackground &= (proc.userId != userId);
}
if (isBackground && !showBackground) {
@@ -1829,15 +1783,15 @@
break;
}
case REPORT_USER_SWITCH_MSG: {
- dispatchUserSwitch((UserState) msg.obj, msg.arg1, msg.arg2);
+ mUserController.dispatchUserSwitch((UserState) msg.obj, msg.arg1, msg.arg2);
break;
}
case CONTINUE_USER_SWITCH_MSG: {
- continueUserSwitch((UserState) msg.obj, msg.arg1, msg.arg2);
+ mUserController.continueUserSwitch((UserState) msg.obj, msg.arg1, msg.arg2);
break;
}
case USER_SWITCH_TIMEOUT_MSG: {
- timeoutUserSwitch((UserState) msg.obj, msg.arg1, msg.arg2);
+ mUserController.timeoutUserSwitch((UserState) msg.obj, msg.arg1, msg.arg2);
break;
}
case IMMERSIVE_MODE_LOCK_MSG: {
@@ -1866,7 +1820,7 @@
}
case START_PROFILES_MSG: {
synchronized (ActivityManagerService.this) {
- startProfilesLocked();
+ mUserController.startProfilesLocked();
}
break;
}
@@ -2057,14 +2011,14 @@
}
} break;
case FOREGROUND_PROFILE_CHANGED_MSG: {
- dispatchForegroundProfileChanged(msg.arg1);
+ mUserController.dispatchForegroundProfileChanged(msg.arg1);
} break;
case REPORT_TIME_TRACKER_MSG: {
AppTimeTracker tracker = (AppTimeTracker)msg.obj;
tracker.deliverResult(mContext);
} break;
case REPORT_USER_SWITCH_COMPLETE_MSG: {
- dispatchUserSwitchComplete(msg.arg1);
+ mUserController.dispatchUserSwitchComplete(msg.arg1);
} break;
case SHUTDOWN_UI_AUTOMATION_CONNECTION_MSG: {
IUiAutomationConnection connection = (IUiAutomationConnection) msg.obj;
@@ -2386,10 +2340,7 @@
mGrantFile = new AtomicFile(new File(systemDir, "urigrants.xml"));
- // User 0 is the first and only user that runs at boot.
- mStartedUsers.put(UserHandle.USER_SYSTEM, new UserState(UserHandle.SYSTEM, true));
- mUserLru.add(UserHandle.USER_SYSTEM);
- updateStartedUserArrayLocked();
+ mUserController = new UserController(this);
GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version",
ConfigurationInfo.GL_ES_VERSION_UNDEFINED);
@@ -5566,16 +5517,6 @@
null, false, false, MY_PID, Process.SYSTEM_UID, UserHandle.getUserId(uid));
}
- private void forceStopUserLocked(int userId, String reason) {
- forceStopPackageLocked(null, -1, false, false, true, false, false, userId, reason);
- Intent intent = new Intent(Intent.ACTION_USER_STOPPED);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
- | Intent.FLAG_RECEIVER_FOREGROUND);
- intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
- broadcastIntentLocked(null, null, intent,
- null, null, 0, null, null, null, AppOpsManager.OP_NONE,
- null, false, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
- }
private final boolean killPackageProcessesLocked(String packageName, int appId,
int userId, int minOomAdj, boolean callerWillRestart, boolean allowRestart,
@@ -5739,7 +5680,7 @@
}
- private final boolean forceStopPackageLocked(String packageName, int appId,
+ final boolean forceStopPackageLocked(String packageName, int appId,
boolean callerWillRestart, boolean purgeCache, boolean doit,
boolean evenPersistent, boolean uninstalling, int userId, String reason) {
int i;
@@ -6460,32 +6401,18 @@
|| "".equals(SystemProperties.get("vold.encrypt_progress"))) {
SystemProperties.set("dev.bootcomplete", "1");
}
- for (int i=0; i<mStartedUsers.size(); i++) {
- UserState uss = mStartedUsers.valueAt(i);
- if (uss.mState == UserState.STATE_BOOTING) {
- uss.mState = UserState.STATE_RUNNING;
- final int userId = mStartedUsers.keyAt(i);
- Intent intent = new Intent(Intent.ACTION_BOOT_COMPLETED, null);
- intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
- intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT);
- broadcastIntentLocked(null, null, intent, null,
- new IIntentReceiver.Stub() {
- @Override
- public void performReceive(Intent intent, int resultCode,
- String data, Bundle extras, boolean ordered,
- boolean sticky, int sendingUser) {
- synchronized (ActivityManagerService.this) {
- requestPssAllProcsLocked(SystemClock.uptimeMillis(),
- true, false);
- }
- }
- },
- 0, null, null,
- new String[] {android.Manifest.permission.RECEIVE_BOOT_COMPLETED},
- AppOpsManager.OP_NONE, null, true, false,
- MY_PID, Process.SYSTEM_UID, userId);
- }
- }
+ mUserController.sendBootCompletedLocked(
+ new IIntentReceiver.Stub() {
+ @Override
+ public void performReceive(Intent intent, int resultCode,
+ String data, Bundle extras, boolean ordered,
+ boolean sticky, int sendingUser) {
+ synchronized (ActivityManagerService.this) {
+ requestPssAllProcsLocked(SystemClock.uptimeMillis(),
+ true, false);
+ }
+ }
+ });
scheduleStartProfilesLocked();
}
}
@@ -9492,7 +9419,7 @@
boolean checkedGrants = false;
if (checkUser) {
// Looking for cross-user grants before enforcing the typical cross-users permissions
- int tmpTargetUserId = unsafeConvertIncomingUser(userId);
+ int tmpTargetUserId = unsafeConvertIncomingUserLocked(userId);
if (tmpTargetUserId != UserHandle.getUserId(callingUid)) {
if (checkAuthorityGrants(callingUid, cpi, tmpTargetUserId, checkUser)) {
return null;
@@ -10338,7 +10265,9 @@
int callingPid = Binder.getCallingPid();
long ident = 0;
boolean clearedIdentity = false;
- userId = unsafeConvertIncomingUser(userId);
+ synchronized (this) {
+ userId = unsafeConvertIncomingUserLocked(userId);
+ }
if (canClearIdentity(callingPid, callingUid, userId)) {
clearedIdentity = true;
ident = Binder.clearCallingIdentity();
@@ -11005,8 +10934,9 @@
@Override
public boolean isAssistDataAllowedOnCurrentActivity() {
- int userId = mCurrentUserId;
+ int userId;
synchronized (this) {
+ userId = mUserController.mCurrentUserId;
ActivityRecord activity = getFocusedStack().topActivity();
if (activity == null) {
return false;
@@ -11929,7 +11859,7 @@
// Make sure we have the current profile info, since it is needed for
// security checks.
- updateCurrentProfileIdsLocked();
+ mUserController.updateCurrentProfileIdsLocked();
mRecentTasks.clear();
mRecentTasks.addAll(mTaskPersister.restoreTasksLocked(
@@ -12036,18 +11966,20 @@
retrieveSettings();
loadResourcesOnSystemReady();
-
+ final int currentUserId;
synchronized (this) {
+ currentUserId = mUserController.mCurrentUserId;
readGrantedUriPermissionsLocked();
}
if (goingCallback != null) goingCallback.run();
+
mBatteryStatsService.noteEvent(BatteryStats.HistoryItem.EVENT_USER_RUNNING_START,
- Integer.toString(mCurrentUserId), mCurrentUserId);
+ Integer.toString(currentUserId), currentUserId);
mBatteryStatsService.noteEvent(BatteryStats.HistoryItem.EVENT_USER_FOREGROUND_START,
- Integer.toString(mCurrentUserId), mCurrentUserId);
- mSystemServiceManager.startUser(mCurrentUserId);
+ Integer.toString(currentUserId), currentUserId);
+ mSystemServiceManager.startUser(currentUserId);
synchronized (this) {
if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
@@ -12075,7 +12007,7 @@
// Start up initial activity.
mBooting = true;
- startHomeActivityLocked(mCurrentUserId, "systemReady");
+ startHomeActivityLocked(currentUserId, "systemReady");
try {
if (AppGlobals.getPackageManager().hasSystemUidErrors()) {
@@ -12096,13 +12028,14 @@
Intent intent = new Intent(Intent.ACTION_USER_STARTED);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
| Intent.FLAG_RECEIVER_FOREGROUND);
- intent.putExtra(Intent.EXTRA_USER_HANDLE, mCurrentUserId);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, currentUserId);
broadcastIntentLocked(null, null, intent,
null, null, 0, null, null, null, AppOpsManager.OP_NONE,
- null, false, false, MY_PID, Process.SYSTEM_UID, mCurrentUserId);
+ null, false, false, MY_PID, Process.SYSTEM_UID,
+ currentUserId);
intent = new Intent(Intent.ACTION_USER_STARTING);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- intent.putExtra(Intent.EXTRA_USER_HANDLE, mCurrentUserId);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, currentUserId);
broadcastIntentLocked(null, null, intent,
null, new IIntentReceiver.Stub() {
@Override
@@ -12119,7 +12052,7 @@
Binder.restoreCallingIdentity(ident);
}
mStackSupervisor.resumeTopActivitiesLocked();
- sendUserSwitchBroadcastsLocked(-1, mCurrentUserId);
+ sendUserSwitchBroadcastsLocked(-1, currentUserId);
}
}
@@ -12318,7 +12251,7 @@
// launching the report UI under a different user.
app.errorReportReceiver = null;
- for (int userId : mCurrentProfileIds) {
+ for (int userId : mUserController.mCurrentProfileIds) {
if (app.userId == userId) {
app.errorReportReceiver = ApplicationErrorReport.getErrorReportReceiver(
mContext, app.info.packageName, app.info.flags);
@@ -13775,38 +13708,7 @@
if (dumpPackage == null) {
pw.println();
needSep = false;
- pw.println(" mStartedUsers:");
- for (int i=0; i<mStartedUsers.size(); i++) {
- UserState uss = mStartedUsers.valueAt(i);
- pw.print(" User #"); pw.print(uss.mHandle.getIdentifier());
- pw.print(": "); uss.dump("", pw);
- }
- pw.print(" mStartedUserArray: [");
- for (int i=0; i<mStartedUserArray.length; i++) {
- if (i > 0) pw.print(", ");
- pw.print(mStartedUserArray[i]);
- }
- pw.println("]");
- pw.print(" mUserLru: [");
- for (int i=0; i<mUserLru.size(); i++) {
- if (i > 0) pw.print(", ");
- pw.print(mUserLru.get(i));
- }
- pw.println("]");
- if (dumpAll) {
- pw.print(" mStartedUserArray: "); pw.println(Arrays.toString(mStartedUserArray));
- }
- synchronized (mUserProfileGroupIdsSelfLocked) {
- if (mUserProfileGroupIdsSelfLocked.size() > 0) {
- pw.println(" mUserProfileGroupIds:");
- for (int i=0; i<mUserProfileGroupIdsSelfLocked.size(); i++) {
- pw.print(" User #");
- pw.print(mUserProfileGroupIdsSelfLocked.keyAt(i));
- pw.print(" -> profile #");
- pw.println(mUserProfileGroupIdsSelfLocked.valueAt(i));
- }
- }
- }
+ mUserController.dump(pw, dumpAll);
}
if (mHomeProcess != null && (dumpPackage == null
|| mHomeProcess.pkgList.containsKey(dumpPackage))) {
@@ -16098,9 +16000,9 @@
requireFull ? ALLOW_FULL_ONLY : ALLOW_NON_FULL, name, callerPackage);
}
- int unsafeConvertIncomingUser(int userId) {
+ int unsafeConvertIncomingUserLocked(int userId) {
return (userId == UserHandle.USER_CURRENT || userId == UserHandle.USER_CURRENT_OR_SELF)
- ? mCurrentUserId : userId;
+ ? mUserController.mCurrentUserId : userId;
}
int handleIncomingUser(int callingPid, int callingUid, int userId, boolean allowAll,
@@ -16116,7 +16018,7 @@
// the value the caller will receive and someone else changing it.
// We assume that USER_CURRENT_OR_SELF will use the current user; later
// we will switch to the calling user if access to the current user fails.
- int targetUserId = unsafeConvertIncomingUser(userId);
+ int targetUserId = unsafeConvertIncomingUserLocked(userId);
if (callingUid != 0 && callingUid != Process.SYSTEM_UID) {
final boolean allow;
@@ -16137,14 +16039,7 @@
} else if (allowMode == ALLOW_NON_FULL_IN_PROFILE) {
// We may or may not allow this depending on whether the two users are
// in the same profile.
- synchronized (mUserProfileGroupIdsSelfLocked) {
- int callingProfile = mUserProfileGroupIdsSelfLocked.get(callingUserId,
- UserInfo.NO_PROFILE_GROUP_ID);
- int targetProfile = mUserProfileGroupIdsSelfLocked.get(targetUserId,
- UserInfo.NO_PROFILE_GROUP_ID);
- allow = callingProfile != UserInfo.NO_PROFILE_GROUP_ID
- && callingProfile == targetProfile;
- }
+ allow = mUserController.isSameProfileGroup(callingUserId, targetUserId);
} else {
throw new IllegalArgumentException("Unknown mode: " + allowMode);
}
@@ -16763,7 +16658,7 @@
return receivers;
}
- private final int broadcastIntentLocked(ProcessRecord callerApp,
+ final int broadcastIntentLocked(ProcessRecord callerApp,
String callerPackage, Intent intent, String resolvedType,
IIntentReceiver resultTo, int resultCode, String resultData,
Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle options,
@@ -17080,7 +16975,7 @@
int[] users;
if (userId == UserHandle.USER_ALL) {
// Caller wants broadcast to go to all started users.
- users = mStartedUserArray;
+ users = mUserController.getStartedUserArrayLocked();
} else {
// Caller wants broadcast to go to one specific user.
users = new int[] {userId};
@@ -17667,6 +17562,12 @@
Binder.restoreCallingIdentity(origId);
}
}
+ void updateUserConfigurationLocked() {
+ Configuration configuration = new Configuration(mConfiguration);
+ Settings.System.getConfigurationForUser(mContext.getContentResolver(), configuration,
+ mUserController.mCurrentUserId);
+ updateConfigurationLocked(configuration, null, false);
+ }
boolean updateConfigurationLocked(Configuration values,
ActivityRecord starting, boolean initLocale) {
@@ -17712,7 +17613,8 @@
newConfig.seq = mConfigurationSeq;
mConfiguration = newConfig;
Slog.i(TAG, "Config changes=" + Integer.toHexString(changes) + " " + newConfig);
- mUsageStatsService.reportConfigurationChange(newConfig, mCurrentUserId);
+ mUsageStatsService.reportConfigurationChange(newConfig,
+ mUserController.mCurrentUserId);
//mUsageStatsService.noteStartConfig(newConfig);
final Configuration configCopy = new Configuration(mConfiguration);
@@ -19196,7 +19098,7 @@
String authority) {
if (app == null) return;
if (app.curProcState <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) {
- UserState userState = mStartedUsers.get(app.userId);
+ UserState userState = mUserController.getStartedUserState(app.userId);
if (userState == null) return;
final long now = SystemClock.elapsedRealtime();
Long lastReported = userState.mProviderLastReportedFg.get(authority);
@@ -20076,44 +19978,18 @@
*/
@Override
public boolean startUserInBackground(final int userId) {
- return startUser(userId, /* foreground */ false);
+ return mUserController.startUser(userId, /* foreground */ false);
}
/**
* Start user, if its not already running, and bring it to foreground.
*/
boolean startUserInForeground(final int userId, Dialog dlg) {
- boolean result = startUser(userId, /* foreground */ true);
+ boolean result = mUserController.startUser(userId, /* foreground */ true);
dlg.dismiss();
return result;
}
- /**
- * Refreshes the list of users related to the current user when either a
- * user switch happens or when a new related user is started in the
- * background.
- */
- private void updateCurrentProfileIdsLocked() {
- final List<UserInfo> profiles = getUserManagerLocked().getProfiles(
- mCurrentUserId, false /* enabledOnly */);
- int[] currentProfileIds = new int[profiles.size()]; // profiles will not be null
- for (int i = 0; i < currentProfileIds.length; i++) {
- currentProfileIds[i] = profiles.get(i).id;
- }
- mCurrentProfileIds = currentProfileIds;
-
- synchronized (mUserProfileGroupIdsSelfLocked) {
- mUserProfileGroupIdsSelfLocked.clear();
- final List<UserInfo> users = getUserManagerLocked().getUsers(false);
- for (int i = 0; i < users.size(); i++) {
- UserInfo user = users.get(i);
- if (user.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID) {
- mUserProfileGroupIdsSelfLocked.put(user.id, user.profileGroupId);
- }
- }
- }
- }
-
private Set<Integer> getProfileIdsLocked(int userId) {
Set<Integer> userIds = new HashSet<Integer>();
final List<UserInfo> profiles = getUserManagerLocked().getProfiles(
@@ -20139,7 +20015,7 @@
return false;
}
userName = userInfo.name;
- mTargetUserId = userId;
+ mUserController.mTargetUserId = userId;
}
mUiHandler.removeMessages(START_USER_SWITCH_MSG);
mUiHandler.sendMessage(mUiHandler.obtainMessage(START_USER_SWITCH_MSG, userId, 0, userName));
@@ -20153,186 +20029,6 @@
d.show();
}
- private boolean startUser(final int userId, final boolean foreground) {
- if (checkCallingPermission(INTERACT_ACROSS_USERS_FULL)
- != PackageManager.PERMISSION_GRANTED) {
- String msg = "Permission Denial: switchUser() from pid="
- + Binder.getCallingPid()
- + ", uid=" + Binder.getCallingUid()
- + " requires " + INTERACT_ACROSS_USERS_FULL;
- Slog.w(TAG, msg);
- throw new SecurityException(msg);
- }
-
- if (DEBUG_MU) Slog.i(TAG_MU, "starting userid:" + userId + " fore:" + foreground);
-
- final long ident = Binder.clearCallingIdentity();
- try {
- synchronized (this) {
- final int oldUserId = mCurrentUserId;
- if (oldUserId == userId) {
- return true;
- }
-
- mStackSupervisor.setLockTaskModeLocked(null, ActivityManager.LOCK_TASK_MODE_NONE,
- "startUser", false);
-
- final UserInfo userInfo = getUserManagerLocked().getUserInfo(userId);
- if (userInfo == null) {
- Slog.w(TAG, "No user info for user #" + userId);
- return false;
- }
- if (foreground && userInfo.isManagedProfile()) {
- Slog.w(TAG, "Cannot switch to User #" + userId + ": not a full user");
- return false;
- }
-
- if (foreground) {
- mWindowManager.startFreezingScreen(R.anim.screen_user_exit,
- R.anim.screen_user_enter);
- }
-
- boolean needStart = false;
-
- // 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), false));
- updateStartedUserArrayLocked();
- needStart = true;
- }
-
- final Integer userIdInt = Integer.valueOf(userId);
- mUserLru.remove(userIdInt);
- mUserLru.add(userIdInt);
-
- if (foreground) {
- mCurrentUserId = userId;
- updateUserConfigurationLocked();
- mTargetUserId = UserHandle.USER_NULL; // reset, mCurrentUserId has caught up
- updateCurrentProfileIdsLocked();
- mWindowManager.setCurrentUser(userId, mCurrentProfileIds);
- // Once the internal notion of the active user has switched, we lock the device
- // with the option to show the user switcher on the keyguard.
- mWindowManager.lockNow(null);
- } else {
- final Integer currentUserIdInt = Integer.valueOf(mCurrentUserId);
- updateCurrentProfileIdsLocked();
- mWindowManager.setCurrentProfileIds(mCurrentProfileIds);
- mUserLru.remove(currentUserIdInt);
- mUserLru.add(currentUserIdInt);
- }
-
- final UserState uss = mStartedUsers.get(userId);
-
- // Make sure user is in the started state. If it is currently
- // stopping, we need to knock that off.
- if (uss.mState == UserState.STATE_STOPPING) {
- // If we are stopping, we haven't sent ACTION_SHUTDOWN,
- // so we can just fairly silently bring the user back from
- // the almost-dead.
- uss.mState = UserState.STATE_RUNNING;
- updateStartedUserArrayLocked();
- needStart = true;
- } else if (uss.mState == UserState.STATE_SHUTDOWN) {
- // This means ACTION_SHUTDOWN has been sent, so we will
- // need to treat this as a new boot of the user.
- uss.mState = UserState.STATE_BOOTING;
- updateStartedUserArrayLocked();
- needStart = true;
- }
-
- if (uss.mState == UserState.STATE_BOOTING) {
- // Booting up a new user, need to tell system services about it.
- // Note that this is on the same handler as scheduling of broadcasts,
- // which is important because it needs to go first.
- mHandler.sendMessage(mHandler.obtainMessage(SYSTEM_USER_START_MSG, userId, 0));
- }
-
- if (foreground) {
- mHandler.sendMessage(mHandler.obtainMessage(SYSTEM_USER_CURRENT_MSG, userId,
- oldUserId));
- mHandler.removeMessages(REPORT_USER_SWITCH_MSG);
- mHandler.removeMessages(USER_SWITCH_TIMEOUT_MSG);
- mHandler.sendMessage(mHandler.obtainMessage(REPORT_USER_SWITCH_MSG,
- oldUserId, userId, uss));
- mHandler.sendMessageDelayed(mHandler.obtainMessage(USER_SWITCH_TIMEOUT_MSG,
- oldUserId, userId, uss), USER_SWITCH_TIMEOUT);
- }
-
- if (needStart) {
- // Send USER_STARTED broadcast
- Intent intent = new Intent(Intent.ACTION_USER_STARTED);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
- | Intent.FLAG_RECEIVER_FOREGROUND);
- intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
- broadcastIntentLocked(null, null, intent,
- null, null, 0, null, null, null, AppOpsManager.OP_NONE,
- null, false, false, MY_PID, Process.SYSTEM_UID, userId);
- }
-
- if ((userInfo.flags&UserInfo.FLAG_INITIALIZED) == 0) {
- if (userId != UserHandle.USER_SYSTEM) {
- Intent intent = new Intent(Intent.ACTION_USER_INITIALIZE);
- intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
- broadcastIntentLocked(null, null, intent, null,
- new IIntentReceiver.Stub() {
- public void performReceive(Intent intent, int resultCode,
- String data, Bundle extras, boolean ordered,
- boolean sticky, int sendingUser) {
- onUserInitialized(uss, foreground, oldUserId, userId);
- }
- }, 0, null, null, null, AppOpsManager.OP_NONE,
- null, true, false, MY_PID, Process.SYSTEM_UID, userId);
- uss.initializing = true;
- } else {
- getUserManagerLocked().makeInitialized(userInfo.id);
- }
- }
-
- if (foreground) {
- if (!uss.initializing) {
- moveUserToForegroundLocked(uss, oldUserId, userId);
- }
- } else {
- mStackSupervisor.startBackgroundUserLocked(userId, uss);
- }
-
- if (needStart) {
- Intent intent = new Intent(Intent.ACTION_USER_STARTING);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
- broadcastIntentLocked(null, null, intent,
- null, new IIntentReceiver.Stub() {
- @Override
- public void performReceive(Intent intent, int resultCode,
- String data, Bundle extras, boolean ordered, boolean sticky,
- int sendingUser) throws RemoteException {
- }
- }, 0, null, null,
- new String[] {INTERACT_ACROSS_USERS}, AppOpsManager.OP_NONE,
- null, true, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
- }
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
-
- return true;
- }
-
- void dispatchForegroundProfileChanged(int userId) {
- final int N = mUserSwitchObservers.beginBroadcast();
- for (int i = 0; i < N; i++) {
- try {
- mUserSwitchObservers.getBroadcastItem(i).onForegroundProfileSwitch(userId);
- } catch (RemoteException e) {
- // Ignore
- }
- }
- mUserSwitchObservers.finishBroadcast();
- }
-
void sendUserSwitchBroadcastsLocked(int oldUserId, int newUserId) {
long ident = Binder.clearCallingIdentity();
try {
@@ -20381,150 +20077,6 @@
}
}
- void dispatchUserSwitch(final UserState uss, final int oldUserId,
- final int newUserId) {
- final int N = mUserSwitchObservers.beginBroadcast();
- if (N > 0) {
- final IRemoteCallback callback = new IRemoteCallback.Stub() {
- int mCount = 0;
- @Override
- public void sendResult(Bundle data) throws RemoteException {
- synchronized (ActivityManagerService.this) {
- if (mCurUserSwitchCallback == this) {
- mCount++;
- if (mCount == N) {
- sendContinueUserSwitchLocked(uss, oldUserId, newUserId);
- }
- }
- }
- }
- };
- synchronized (this) {
- uss.switching = true;
- mCurUserSwitchCallback = callback;
- }
- for (int i=0; i<N; i++) {
- try {
- mUserSwitchObservers.getBroadcastItem(i).onUserSwitching(
- newUserId, callback);
- } catch (RemoteException e) {
- }
- }
- } else {
- synchronized (this) {
- sendContinueUserSwitchLocked(uss, oldUserId, newUserId);
- }
- }
- mUserSwitchObservers.finishBroadcast();
- }
-
- void timeoutUserSwitch(UserState uss, int oldUserId, int newUserId) {
- synchronized (this) {
- Slog.w(TAG, "User switch timeout: from " + oldUserId + " to " + newUserId);
- sendContinueUserSwitchLocked(uss, oldUserId, newUserId);
- }
- }
-
- void sendContinueUserSwitchLocked(UserState uss, int oldUserId, int newUserId) {
- mCurUserSwitchCallback = null;
- mHandler.removeMessages(USER_SWITCH_TIMEOUT_MSG);
- mHandler.sendMessage(mHandler.obtainMessage(CONTINUE_USER_SWITCH_MSG,
- oldUserId, newUserId, uss));
- }
-
- void onUserInitialized(UserState uss, boolean foreground, int oldUserId, int newUserId) {
- synchronized (this) {
- if (foreground) {
- moveUserToForegroundLocked(uss, oldUserId, newUserId);
- }
- }
-
- completeSwitchAndInitialize(uss, newUserId, true, false);
- }
-
- void moveUserToForegroundLocked(UserState uss, int oldUserId, int newUserId) {
- boolean homeInFront = mStackSupervisor.switchUserLocked(newUserId, uss);
- if (homeInFront) {
- startHomeActivityLocked(newUserId, "moveUserToFroreground");
- } else {
- mStackSupervisor.resumeTopActivitiesLocked();
- }
- EventLogTags.writeAmSwitchUser(newUserId);
- getUserManagerLocked().onUserForeground(newUserId);
- sendUserSwitchBroadcastsLocked(oldUserId, newUserId);
- }
-
- private void updateUserConfigurationLocked() {
- Configuration configuration = new Configuration(mConfiguration);
- Settings.System.getConfigurationForUser(mContext.getContentResolver(), configuration,
- mCurrentUserId);
- updateConfigurationLocked(configuration, null, false);
- }
-
- void continueUserSwitch(UserState uss, int oldUserId, int newUserId) {
- completeSwitchAndInitialize(uss, newUserId, false, true);
- }
-
- void completeSwitchAndInitialize(UserState uss, int newUserId,
- boolean clearInitializing, boolean clearSwitching) {
- boolean unfrozen = false;
- synchronized (this) {
- if (clearInitializing) {
- uss.initializing = false;
- getUserManagerLocked().makeInitialized(uss.mHandle.getIdentifier());
- }
- if (clearSwitching) {
- uss.switching = false;
- }
- if (!uss.switching && !uss.initializing) {
- mWindowManager.stopFreezingScreen();
- unfrozen = true;
- }
- }
- if (unfrozen) {
- mHandler.removeMessages(REPORT_USER_SWITCH_COMPLETE_MSG);
- mHandler.sendMessage(mHandler.obtainMessage(REPORT_USER_SWITCH_COMPLETE_MSG,
- newUserId, 0));
- }
- stopGuestUserIfBackground();
- }
-
- /** Called on handler thread */
- void dispatchUserSwitchComplete(int userId) {
- final int observerCount = mUserSwitchObservers.beginBroadcast();
- for (int i = 0; i < observerCount; i++) {
- try {
- mUserSwitchObservers.getBroadcastItem(i).onUserSwitchComplete(userId);
- } catch (RemoteException e) {
- }
- }
- mUserSwitchObservers.finishBroadcast();
- }
-
- /**
- * Stops the guest user if it has gone to the background.
- */
- private void stopGuestUserIfBackground() {
- synchronized (this) {
- final int num = mUserLru.size();
- for (int i = 0; i < num; i++) {
- Integer oldUserId = mUserLru.get(i);
- UserState oldUss = mStartedUsers.get(oldUserId);
- if (oldUserId == UserHandle.USER_SYSTEM || oldUserId == mCurrentUserId
- || oldUss.mState == UserState.STATE_STOPPING
- || oldUss.mState == UserState.STATE_SHUTDOWN) {
- continue;
- }
- UserInfo userInfo = mUserManager.getUserInfo(oldUserId);
- if (userInfo.isGuest()) {
- // This is a user to be stopped.
- stopUserLocked(oldUserId, null);
- break;
- }
- }
- }
- }
-
void scheduleStartProfilesLocked() {
if (!mHandler.hasMessages(START_PROFILES_MSG)) {
mHandler.sendMessageDelayed(mHandler.obtainMessage(START_PROFILES_MSG),
@@ -20532,229 +20084,9 @@
}
}
- void startProfilesLocked() {
- if (DEBUG_MU) Slog.i(TAG_MU, "startProfilesLocked");
- List<UserInfo> profiles = getUserManagerLocked().getProfiles(
- mCurrentUserId, false /* enabledOnly */);
- List<UserInfo> toStart = new ArrayList<UserInfo>(profiles.size());
- for (UserInfo user : profiles) {
- if ((user.flags & UserInfo.FLAG_INITIALIZED) == UserInfo.FLAG_INITIALIZED
- && user.id != mCurrentUserId) {
- toStart.add(user);
- }
- }
- final int n = toStart.size();
- int i = 0;
- for (; i < n && i < (MAX_RUNNING_USERS - 1); ++i) {
- startUserInBackground(toStart.get(i).id);
- }
- if (i < n) {
- Slog.w(TAG_MU, "More profiles than MAX_RUNNING_USERS");
- }
- }
-
- void finishUserBoot(UserState uss) {
- synchronized (this) {
- if (uss.mState == UserState.STATE_BOOTING
- && mStartedUsers.get(uss.mHandle.getIdentifier()) == uss) {
- uss.mState = UserState.STATE_RUNNING;
- final int userId = uss.mHandle.getIdentifier();
- Intent intent = new Intent(Intent.ACTION_BOOT_COMPLETED, null);
- intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
- intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT);
- broadcastIntentLocked(null, null, intent,
- null, null, 0, null, null,
- new String[] {android.Manifest.permission.RECEIVE_BOOT_COMPLETED},
- AppOpsManager.OP_NONE, null, true, false, MY_PID, Process.SYSTEM_UID,
- userId);
- }
- }
- }
-
- void finishUserSwitch(UserState uss) {
- synchronized (this) {
- finishUserBoot(uss);
-
- startProfilesLocked();
-
- 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.
- 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++;
- }
- }
- }
-
@Override
public int stopUser(final int userId, final IStopUserCallback callback) {
- if (checkCallingPermission(INTERACT_ACROSS_USERS_FULL)
- != PackageManager.PERMISSION_GRANTED) {
- String msg = "Permission Denial: switchUser() from pid="
- + Binder.getCallingPid()
- + ", uid=" + Binder.getCallingUid()
- + " requires " + INTERACT_ACROSS_USERS_FULL;
- Slog.w(TAG, msg);
- throw new SecurityException(msg);
- }
- if (userId < 0 || userId == UserHandle.USER_SYSTEM) {
- throw new IllegalArgumentException("Can't stop system user " + userId);
- }
- enforceShellRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, userId);
- synchronized (this) {
- return stopUserLocked(userId, callback);
- }
- }
-
- private int stopUserLocked(final int userId, final IStopUserCallback callback) {
- if (DEBUG_MU) Slog.i(TAG_MU, "stopUserLocked userId=" + userId);
- if (mCurrentUserId == userId && mTargetUserId == UserHandle.USER_NULL) {
- return ActivityManager.USER_OP_IS_CURRENT;
- }
-
- final UserState uss = mStartedUsers.get(userId);
- if (uss == null) {
- // User is not started, nothing to do... but we do need to
- // callback if requested.
- if (callback != null) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- try {
- callback.userStopped(userId);
- } catch (RemoteException e) {
- }
- }
- });
- }
- return ActivityManager.USER_OP_SUCCESS;
- }
-
- if (callback != null) {
- uss.mStopCallbacks.add(callback);
- }
-
- if (uss.mState != UserState.STATE_STOPPING
- && uss.mState != UserState.STATE_SHUTDOWN) {
- uss.mState = UserState.STATE_STOPPING;
- updateStartedUserArrayLocked();
-
- long ident = Binder.clearCallingIdentity();
- try {
- // We are going to broadcast ACTION_USER_STOPPING and then
- // once that is done send a final ACTION_SHUTDOWN and then
- // stop the user.
- final Intent stoppingIntent = new Intent(Intent.ACTION_USER_STOPPING);
- stoppingIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- stoppingIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
- stoppingIntent.putExtra(Intent.EXTRA_SHUTDOWN_USERSPACE_ONLY, true);
- final Intent shutdownIntent = new Intent(Intent.ACTION_SHUTDOWN);
- // This is the result receiver for the final shutdown broadcast.
- final IIntentReceiver shutdownReceiver = new IIntentReceiver.Stub() {
- @Override
- public void performReceive(Intent intent, int resultCode, String data,
- Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
- finishUserStop(uss);
- }
- };
- // This is the result receiver for the initial stopping broadcast.
- final IIntentReceiver stoppingReceiver = new IIntentReceiver.Stub() {
- @Override
- public void performReceive(Intent intent, int resultCode, String data,
- Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
- // On to the next.
- synchronized (ActivityManagerService.this) {
- if (uss.mState != UserState.STATE_STOPPING) {
- // Whoops, we are being started back up. Abort, abort!
- return;
- }
- uss.mState = UserState.STATE_SHUTDOWN;
- }
- mBatteryStatsService.noteEvent(
- BatteryStats.HistoryItem.EVENT_USER_RUNNING_FINISH,
- Integer.toString(userId), userId);
- mSystemServiceManager.stopUser(userId);
- broadcastIntentLocked(null, null, shutdownIntent,
- null, shutdownReceiver, 0, null, null, null, AppOpsManager.OP_NONE,
- null, true, false, MY_PID, Process.SYSTEM_UID, userId);
- }
- };
- // Kick things off.
- broadcastIntentLocked(null, null, stoppingIntent,
- null, stoppingReceiver, 0, null, null,
- new String[] {INTERACT_ACROSS_USERS}, AppOpsManager.OP_NONE,
- null, true, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
- return ActivityManager.USER_OP_SUCCESS;
- }
-
- void finishUserStop(UserState uss) {
- final int userId = uss.mHandle.getIdentifier();
- boolean stopped;
- ArrayList<IStopUserCallback> callbacks;
- synchronized (this) {
- callbacks = new ArrayList<IStopUserCallback>(uss.mStopCallbacks);
- if (mStartedUsers.get(userId) != uss) {
- stopped = false;
- } else if (uss.mState != UserState.STATE_SHUTDOWN) {
- stopped = false;
- } else {
- stopped = true;
- // User can no longer run.
- mStartedUsers.remove(userId);
- mUserLru.remove(Integer.valueOf(userId));
- updateStartedUserArrayLocked();
-
- // Clean up all state and processes associated with the user.
- // Kill all the processes for the user.
- forceStopUserLocked(userId, "finish user");
- }
- }
-
- for (int i=0; i<callbacks.size(); i++) {
- try {
- if (stopped) callbacks.get(i).userStopped(userId);
- else callbacks.get(i).userStopAborted(userId);
- } catch (RemoteException e) {
- }
- }
-
- if (stopped) {
- mSystemServiceManager.cleanupUser(userId);
- synchronized (this) {
- mStackSupervisor.removeUserLocked(userId);
- }
- }
+ return mUserController.stopUser(userId, callback);
}
void onUserRemovedLocked(int userId) {
@@ -20763,25 +20095,7 @@
@Override
public UserInfo getCurrentUser() {
- if ((checkCallingPermission(INTERACT_ACROSS_USERS)
- != PackageManager.PERMISSION_GRANTED) && (
- checkCallingPermission(INTERACT_ACROSS_USERS_FULL)
- != PackageManager.PERMISSION_GRANTED)) {
- String msg = "Permission Denial: getCurrentUser() from pid="
- + Binder.getCallingPid()
- + ", uid=" + Binder.getCallingUid()
- + " requires " + INTERACT_ACROSS_USERS;
- Slog.w(TAG, msg);
- throw new SecurityException(msg);
- }
- synchronized (this) {
- int userId = mTargetUserId != UserHandle.USER_NULL ? mTargetUserId : mCurrentUserId;
- return getUserManagerLocked().getUserInfo(userId);
- }
- }
-
- int getCurrentUserIdLocked() {
- return mTargetUserId != UserHandle.USER_NULL ? mTargetUserId : mCurrentUserId;
+ return mUserController.getCurrentUser();
}
@Override
@@ -20801,7 +20115,7 @@
}
boolean isUserRunningLocked(int userId, boolean orStopped) {
- UserState state = mStartedUsers.get(userId);
+ UserState state = mUserController.getStartedUserState(userId);
if (state == null) {
return false;
}
@@ -20824,50 +20138,18 @@
throw new SecurityException(msg);
}
synchronized (this) {
- return mStartedUserArray;
- }
- }
-
- private void updateStartedUserArrayLocked() {
- int num = 0;
- for (int i=0; i<mStartedUsers.size(); i++) {
- UserState uss = mStartedUsers.valueAt(i);
- // This list does not include stopping users.
- if (uss.mState != UserState.STATE_STOPPING
- && uss.mState != UserState.STATE_SHUTDOWN) {
- num++;
- }
- }
- mStartedUserArray = new int[num];
- num = 0;
- for (int i=0; i<mStartedUsers.size(); i++) {
- UserState uss = mStartedUsers.valueAt(i);
- if (uss.mState != UserState.STATE_STOPPING
- && uss.mState != UserState.STATE_SHUTDOWN) {
- mStartedUserArray[num] = mStartedUsers.keyAt(i);
- num++;
- }
+ return mUserController.getStartedUserArrayLocked();
}
}
@Override
public void registerUserSwitchObserver(IUserSwitchObserver observer) {
- if (checkCallingPermission(INTERACT_ACROSS_USERS_FULL)
- != PackageManager.PERMISSION_GRANTED) {
- String msg = "Permission Denial: registerUserSwitchObserver() from pid="
- + Binder.getCallingPid()
- + ", uid=" + Binder.getCallingUid()
- + " requires " + INTERACT_ACROSS_USERS_FULL;
- Slog.w(TAG, msg);
- throw new SecurityException(msg);
- }
-
- mUserSwitchObservers.register(observer);
+ mUserController.registerUserSwitchObserver(observer);
}
@Override
public void unregisterUserSwitchObserver(IUserSwitchObserver observer) {
- mUserSwitchObservers.unregister(observer);
+ mUserController.unregisterUserSwitchObserver(observer);
}
int[] getUsersLocked() {
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 1721470..8bf1d22 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -366,7 +366,7 @@
mHandler = new ActivityStackHandler(mService.mHandler.getLooper());
mWindowManager = mService.mWindowManager;
mStackId = activityContainer.mStackId;
- mCurrentUser = mService.mCurrentUserId;
+ mCurrentUser = mService.mUserController.mCurrentUserId;
mRecentTasks = recentTasks;
mTaskPositioner = mStackId == FREEFORM_WORKSPACE_STACK_ID
? new LaunchingTaskPositioner() : null;
@@ -1819,7 +1819,7 @@
// Make sure that the user who owns this activity is started. If not,
// we will just leave it as is because someone should be bringing
// another user's activities to the top of the stack.
- if (mService.mStartedUsers.get(next.userId) == null) {
+ if (!mService.mUserController.hasStartedUserState(next.userId)) {
Slog.w(TAG, "Skipping resume of top activity " + next
+ ": user " + next.userId + " is stopped");
if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 54ac58a..99f7ec6 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -97,7 +97,6 @@
import android.service.voice.IVoiceInteractionSession;
import android.util.ArrayMap;
import android.util.ArraySet;
-import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.Slog;
import android.util.SparseArray;
@@ -2693,7 +2692,7 @@
// Complete user switch
if (startingUsers != null) {
for (int i = 0; i < startingUsers.size(); i++) {
- mService.finishUserSwitch(startingUsers.get(i));
+ mService.mUserController.finishUserSwitch(startingUsers.get(i));
}
}
// Complete starting up of background users
@@ -2701,7 +2700,7 @@
startingUsers = new ArrayList<UserState>(mStartingBackgroundUsers);
mStartingBackgroundUsers.clear();
for (int i = 0; i < startingUsers.size(); i++) {
- mService.finishUserBoot(startingUsers.get(i));
+ mService.mUserController.finishUserBoot(startingUsers.get(i));
}
}
}
@@ -3756,10 +3755,7 @@
/** Checks whether the userid is a profile of the current user. */
boolean isCurrentProfileLocked(int userId) {
if (userId == mCurrentUser) return true;
- for (int i = 0; i < mService.mCurrentProfileIds.length; i++) {
- if (mService.mCurrentProfileIds[i] == userId) return true;
- }
- return false;
+ return mService.mUserController.isCurrentProfileLocked(userId);
}
/** Checks whether the activity should be shown for current user. */
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index 5b46799..6ed880e 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -248,7 +248,7 @@
boolean sendFinish = finishedReceiver != null;
int userId = key.userId;
if (userId == UserHandle.USER_CURRENT) {
- userId = owner.getCurrentUserIdLocked();
+ userId = owner.mUserController.getCurrentUserIdLocked();
}
switch (key.type) {
case ActivityManager.INTENT_SENDER_ACTIVITY:
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
new file mode 100644
index 0000000..ff74d83
--- /dev/null
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -0,0 +1,862 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+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_IS_CURRENT;
+import static android.app.ActivityManager.USER_OP_SUCCESS;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.ActivityManagerService.REPORT_USER_SWITCH_COMPLETE_MSG;
+import static com.android.server.am.ActivityManagerService.REPORT_USER_SWITCH_MSG;
+import static com.android.server.am.ActivityManagerService.SYSTEM_USER_CURRENT_MSG;
+import static com.android.server.am.ActivityManagerService.SYSTEM_USER_START_MSG;
+import static com.android.server.am.ActivityManagerService.USER_SWITCH_TIMEOUT_MSG;
+
+import android.app.ActivityManager;
+import android.app.AppOpsManager;
+import android.app.IStopUserCallback;
+import android.app.IUserSwitchObserver;
+import android.content.IIntentReceiver;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.os.BatteryStats;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IRemoteCallback;
+import android.os.Process;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
+
+import com.android.internal.R;
+import com.android.internal.util.ArrayUtils;
+import com.android.server.pm.UserManagerService;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Helper class for {@link ActivityManagerService} responsible for multi-user functionality.
+ */
+final class UserController {
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "UserController" : TAG_AM;
+ // Maximum number of users we allow to be running at a time.
+ static final int MAX_RUNNING_USERS = 3;
+
+ // Amount of time we wait for observers to handle a user switch before
+ // giving up on them and unfreezing the screen.
+ static final int USER_SWITCH_TIMEOUT = 2 * 1000;
+
+ private final ActivityManagerService mService;
+ private final Handler mHandler;
+
+ // Holds the current foreground user's id
+ int mCurrentUserId = 0;
+ // Holds the target user's id during a user switch
+ int mTargetUserId = UserHandle.USER_NULL;
+
+ /**
+ * Which users have been started, so are allowed to run code.
+ */
+ private final SparseArray<UserState> mStartedUsers = new SparseArray<>();
+ /**
+ * LRU list of history of current users. Most recently current is at the end.
+ */
+ private final ArrayList<Integer> mUserLru = new ArrayList<>();
+
+ /**
+ * Constant array of the users that are currently started.
+ */
+ private int[] mStartedUserArray = new int[] { 0 };
+
+ // If there are multiple profiles for the current user, their ids are here
+ // Currently only the primary user can have managed profiles
+ int[] mCurrentProfileIds = new int[] {}; // Accessed by ActivityStack
+
+ /**
+ * Mapping from each known user ID to the profile group ID it is associated with.
+ */
+ private final SparseIntArray mUserProfileGroupIdsSelfLocked = new SparseIntArray();
+
+ /**
+ * Registered observers of the user switching mechanics.
+ */
+ final RemoteCallbackList<IUserSwitchObserver> mUserSwitchObservers
+ = new RemoteCallbackList<>();
+
+ /**
+ * Currently active user switch.
+ */
+ Object mCurUserSwitchCallback;
+
+ UserController(ActivityManagerService service) {
+ mService = service;
+ mHandler = mService.mHandler;
+ // User 0 is the first and only user that runs at boot.
+ mStartedUsers.put(UserHandle.USER_SYSTEM, new UserState(UserHandle.SYSTEM, true));
+ mUserLru.add(UserHandle.USER_SYSTEM);
+ updateStartedUserArrayLocked();
+ }
+
+ void finishUserSwitch(UserState uss) {
+ synchronized (mService) {
+ finishUserBoot(uss);
+
+ startProfilesLocked();
+
+ 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.
+ 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++;
+ }
+ }
+ }
+
+ void finishUserBoot(UserState uss) {
+ synchronized (mService) {
+ if (uss.mState == UserState.STATE_BOOTING
+ && mStartedUsers.get(uss.mHandle.getIdentifier()) == uss) {
+ uss.mState = UserState.STATE_RUNNING;
+ final int userId = uss.mHandle.getIdentifier();
+ Intent intent = new Intent(Intent.ACTION_BOOT_COMPLETED, null);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT);
+ mService.broadcastIntentLocked(null, null, intent,
+ null, null, 0, null, null,
+ new String[]{android.Manifest.permission.RECEIVE_BOOT_COMPLETED},
+ AppOpsManager.OP_NONE, null, true, false, ActivityManagerService.MY_PID,
+ Process.SYSTEM_UID, userId);
+ }
+ }
+ }
+
+ int stopUser(final int userId, final IStopUserCallback callback) {
+ if (mService.checkCallingPermission(INTERACT_ACROSS_USERS_FULL)
+ != PackageManager.PERMISSION_GRANTED) {
+ String msg = "Permission Denial: switchUser() from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid()
+ + " requires " + INTERACT_ACROSS_USERS_FULL;
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+ if (userId < 0 || userId == UserHandle.USER_SYSTEM) {
+ throw new IllegalArgumentException("Can't stop system user " + userId);
+ }
+ mService.enforceShellRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES,
+ userId);
+ synchronized (mService) {
+ return stopUserLocked(userId, 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) {
+ return USER_OP_IS_CURRENT;
+ }
+
+ final UserState uss = mStartedUsers.get(userId);
+ if (uss == null) {
+ // User is not started, nothing to do... but we do need to
+ // callback if requested.
+ if (callback != null) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ callback.userStopped(userId);
+ } catch (RemoteException e) {
+ }
+ }
+ });
+ }
+ return USER_OP_SUCCESS;
+ }
+
+ if (callback != null) {
+ uss.mStopCallbacks.add(callback);
+ }
+
+ if (uss.mState != UserState.STATE_STOPPING
+ && uss.mState != UserState.STATE_SHUTDOWN) {
+ uss.mState = UserState.STATE_STOPPING;
+ updateStartedUserArrayLocked();
+
+ long ident = Binder.clearCallingIdentity();
+ try {
+ // We are going to broadcast ACTION_USER_STOPPING and then
+ // once that is done send a final ACTION_SHUTDOWN and then
+ // stop the user.
+ final Intent stoppingIntent = new Intent(Intent.ACTION_USER_STOPPING);
+ stoppingIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ stoppingIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ stoppingIntent.putExtra(Intent.EXTRA_SHUTDOWN_USERSPACE_ONLY, true);
+ final Intent shutdownIntent = new Intent(Intent.ACTION_SHUTDOWN);
+ // This is the result receiver for the final shutdown broadcast.
+ final IIntentReceiver shutdownReceiver = new IIntentReceiver.Stub() {
+ @Override
+ public void performReceive(Intent intent, int resultCode, String data,
+ Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
+ finishUserStop(uss);
+ }
+ };
+ // This is the result receiver for the initial stopping broadcast.
+ final IIntentReceiver stoppingReceiver = new IIntentReceiver.Stub() {
+ @Override
+ public void performReceive(Intent intent, int resultCode, String data,
+ Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
+ // On to the next.
+ synchronized (mService) {
+ if (uss.mState != UserState.STATE_STOPPING) {
+ // Whoops, we are being started back up. Abort, abort!
+ return;
+ }
+ uss.mState = UserState.STATE_SHUTDOWN;
+ }
+ mService.mBatteryStatsService.noteEvent(
+ BatteryStats.HistoryItem.EVENT_USER_RUNNING_FINISH,
+ Integer.toString(userId), userId);
+ mService.mSystemServiceManager.stopUser(userId);
+ mService.broadcastIntentLocked(null, null, shutdownIntent,
+ null, shutdownReceiver, 0, null, null, null, AppOpsManager.OP_NONE,
+ null, true, false, ActivityManagerService.MY_PID,
+ android.os.Process.SYSTEM_UID, userId);
+ }
+ };
+ // Kick things off.
+ mService.broadcastIntentLocked(null, null, stoppingIntent,
+ null, stoppingReceiver, 0, null, null,
+ new String[]{INTERACT_ACROSS_USERS}, AppOpsManager.OP_NONE,
+ null, true, false, ActivityManagerService.MY_PID, Process.SYSTEM_UID,
+ UserHandle.USER_ALL);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ return USER_OP_SUCCESS;
+ }
+
+ void finishUserStop(UserState uss) {
+ final int userId = uss.mHandle.getIdentifier();
+ boolean stopped;
+ ArrayList<IStopUserCallback> callbacks;
+ synchronized (mService) {
+ callbacks = new ArrayList<>(uss.mStopCallbacks);
+ if (mStartedUsers.get(userId) != uss) {
+ stopped = false;
+ } else if (uss.mState != UserState.STATE_SHUTDOWN) {
+ stopped = false;
+ } else {
+ stopped = true;
+ // User can no longer run.
+ mStartedUsers.remove(userId);
+ mUserLru.remove(Integer.valueOf(userId));
+ updateStartedUserArrayLocked();
+
+ // Clean up all state and processes associated with the user.
+ // Kill all the processes for the user.
+ forceStopUserLocked(userId, "finish user");
+ }
+ }
+
+ for (int i = 0; i < callbacks.size(); i++) {
+ try {
+ if (stopped) callbacks.get(i).userStopped(userId);
+ else callbacks.get(i).userStopAborted(userId);
+ } catch (RemoteException e) {
+ }
+ }
+
+ if (stopped) {
+ mService.mSystemServiceManager.cleanupUser(userId);
+ synchronized (mService) {
+ mService.mStackSupervisor.removeUserLocked(userId);
+ }
+ }
+ }
+
+ private void forceStopUserLocked(int userId, String reason) {
+ mService.forceStopPackageLocked(null, -1, false, false, true, false, false,
+ userId, reason);
+ Intent intent = new Intent(Intent.ACTION_USER_STOPPED);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
+ | Intent.FLAG_RECEIVER_FOREGROUND);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ mService.broadcastIntentLocked(null, null, intent,
+ null, null, 0, null, null, null, AppOpsManager.OP_NONE,
+ null, false, false, ActivityManagerService.MY_PID, Process.SYSTEM_UID,
+ UserHandle.USER_ALL);
+ }
+
+
+ /**
+ * Stops the guest user if it has gone to the background.
+ */
+ private void stopGuestUserIfBackground() {
+ synchronized (mService) {
+ final int num = mUserLru.size();
+ for (int i = 0; i < num; i++) {
+ Integer oldUserId = mUserLru.get(i);
+ UserState oldUss = mStartedUsers.get(oldUserId);
+ if (oldUserId == UserHandle.USER_SYSTEM || oldUserId == mCurrentUserId
+ || oldUss.mState == UserState.STATE_STOPPING
+ || oldUss.mState == UserState.STATE_SHUTDOWN) {
+ continue;
+ }
+ UserInfo userInfo = getUserManagerLocked().getUserInfo(oldUserId);
+ if (userInfo.isGuest()) {
+ // This is a user to be stopped.
+ stopUserLocked(oldUserId, null);
+ break;
+ }
+ }
+ }
+ }
+
+ void startProfilesLocked() {
+ if (DEBUG_MU) Slog.i(TAG, "startProfilesLocked");
+ List<UserInfo> profiles = getUserManagerLocked().getProfiles(
+ mCurrentUserId, false /* enabledOnly */);
+ List<UserInfo> profilesToStart = new ArrayList<>(profiles.size());
+ for (UserInfo user : profiles) {
+ if ((user.flags & UserInfo.FLAG_INITIALIZED) == UserInfo.FLAG_INITIALIZED
+ && user.id != mCurrentUserId) {
+ profilesToStart.add(user);
+ }
+ }
+ final int profilesToStartSize = profilesToStart.size();
+ int i = 0;
+ for (; i < profilesToStartSize && i < (MAX_RUNNING_USERS - 1); ++i) {
+ startUser(profilesToStart.get(i).id, /* foreground= */ false);
+ }
+ if (i < profilesToStartSize) {
+ Slog.w(TAG, "More profiles than MAX_RUNNING_USERS");
+ }
+ }
+
+ private UserManagerService getUserManagerLocked() {
+ return mService.getUserManagerLocked();
+ }
+
+ boolean startUser(final int userId, final boolean foreground) {
+ if (mService.checkCallingPermission(INTERACT_ACROSS_USERS_FULL)
+ != PackageManager.PERMISSION_GRANTED) {
+ String msg = "Permission Denial: switchUser() from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid()
+ + " requires " + INTERACT_ACROSS_USERS_FULL;
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+
+ if (DEBUG_MU) Slog.i(TAG, "starting userid:" + userId + " fore:" + foreground);
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ synchronized (mService) {
+ final int oldUserId = mCurrentUserId;
+ if (oldUserId == userId) {
+ return true;
+ }
+
+ mService.mStackSupervisor.setLockTaskModeLocked(null,
+ ActivityManager.LOCK_TASK_MODE_NONE, "startUser", false);
+
+ final UserInfo userInfo = getUserManagerLocked().getUserInfo(userId);
+ if (userInfo == null) {
+ Slog.w(TAG, "No user info for user #" + userId);
+ return false;
+ }
+ if (foreground && userInfo.isManagedProfile()) {
+ Slog.w(TAG, "Cannot switch to User #" + userId + ": not a full user");
+ return false;
+ }
+
+ if (foreground) {
+ mService.mWindowManager.startFreezingScreen(
+ R.anim.screen_user_exit, R.anim.screen_user_enter);
+ }
+
+ boolean needStart = false;
+
+ // 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), false));
+ updateStartedUserArrayLocked();
+ needStart = true;
+ }
+
+ final Integer userIdInt = userId;
+ mUserLru.remove(userIdInt);
+ mUserLru.add(userIdInt);
+
+ if (foreground) {
+ mCurrentUserId = userId;
+ mService.updateUserConfigurationLocked();
+ mTargetUserId = UserHandle.USER_NULL; // reset, mCurrentUserId has caught up
+ updateCurrentProfileIdsLocked();
+ mService.mWindowManager.setCurrentUser(userId, mCurrentProfileIds);
+ // Once the internal notion of the active user has switched, we lock the device
+ // with the option to show the user switcher on the keyguard.
+ mService.mWindowManager.lockNow(null);
+ } else {
+ final Integer currentUserIdInt = mCurrentUserId;
+ updateCurrentProfileIdsLocked();
+ mService.mWindowManager.setCurrentProfileIds(mCurrentProfileIds);
+ mUserLru.remove(currentUserIdInt);
+ mUserLru.add(currentUserIdInt);
+ }
+
+ final UserState uss = mStartedUsers.get(userId);
+
+ // Make sure user is in the started state. If it is currently
+ // stopping, we need to knock that off.
+ if (uss.mState == UserState.STATE_STOPPING) {
+ // If we are stopping, we haven't sent ACTION_SHUTDOWN,
+ // so we can just fairly silently bring the user back from
+ // the almost-dead.
+ uss.mState = UserState.STATE_RUNNING;
+ updateStartedUserArrayLocked();
+ needStart = true;
+ } else if (uss.mState == UserState.STATE_SHUTDOWN) {
+ // This means ACTION_SHUTDOWN has been sent, so we will
+ // need to treat this as a new boot of the user.
+ uss.mState = UserState.STATE_BOOTING;
+ updateStartedUserArrayLocked();
+ needStart = true;
+ }
+
+ if (uss.mState == UserState.STATE_BOOTING) {
+ // Booting up a new user, need to tell system services about it.
+ // Note that this is on the same handler as scheduling of broadcasts,
+ // which is important because it needs to go first.
+ mHandler.sendMessage(mHandler.obtainMessage(SYSTEM_USER_START_MSG, userId, 0));
+ }
+
+ if (foreground) {
+ mHandler.sendMessage(mHandler.obtainMessage(SYSTEM_USER_CURRENT_MSG, userId,
+ oldUserId));
+ mHandler.removeMessages(REPORT_USER_SWITCH_MSG);
+ mHandler.removeMessages(USER_SWITCH_TIMEOUT_MSG);
+ mHandler.sendMessage(mHandler.obtainMessage(REPORT_USER_SWITCH_MSG,
+ oldUserId, userId, uss));
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(USER_SWITCH_TIMEOUT_MSG,
+ oldUserId, userId, uss), USER_SWITCH_TIMEOUT);
+ }
+
+ if (needStart) {
+ // Send USER_STARTED broadcast
+ Intent intent = new Intent(Intent.ACTION_USER_STARTED);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
+ | Intent.FLAG_RECEIVER_FOREGROUND);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ mService.broadcastIntentLocked(null, null, intent,
+ null, null, 0, null, null, null, AppOpsManager.OP_NONE,
+ null, false, false, ActivityManagerService.MY_PID, Process.SYSTEM_UID,
+ userId);
+ }
+
+ if ((userInfo.flags&UserInfo.FLAG_INITIALIZED) == 0) {
+ if (userId != UserHandle.USER_SYSTEM) {
+ Intent intent = new Intent(Intent.ACTION_USER_INITIALIZE);
+ intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ mService.broadcastIntentLocked(null, null, intent, null,
+ new IIntentReceiver.Stub() {
+ public void performReceive(Intent intent, int resultCode,
+ String data, Bundle extras, boolean ordered,
+ boolean sticky, int sendingUser) {
+ onUserInitialized(uss, foreground, oldUserId, userId);
+ }
+ }, 0, null, null, null, AppOpsManager.OP_NONE,
+ null, true, false, ActivityManagerService.MY_PID,
+ Process.SYSTEM_UID, userId);
+ uss.initializing = true;
+ } else {
+ getUserManagerLocked().makeInitialized(userInfo.id);
+ }
+ }
+
+ if (foreground) {
+ if (!uss.initializing) {
+ moveUserToForegroundLocked(uss, oldUserId, userId);
+ }
+ } else {
+ mService.mStackSupervisor.startBackgroundUserLocked(userId, uss);
+ }
+
+ if (needStart) {
+ Intent intent = new Intent(Intent.ACTION_USER_STARTING);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ mService.broadcastIntentLocked(null, null, intent,
+ null, new IIntentReceiver.Stub() {
+ @Override
+ public void performReceive(Intent intent, int resultCode,
+ String data, Bundle extras, boolean ordered, boolean sticky,
+ int sendingUser) throws RemoteException {
+ }
+ }, 0, null, null,
+ new String[]{INTERACT_ACROSS_USERS}, AppOpsManager.OP_NONE,
+ null, true, false, ActivityManagerService.MY_PID, Process.SYSTEM_UID,
+ UserHandle.USER_ALL);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+
+ return true;
+ }
+
+ void dispatchForegroundProfileChanged(int userId) {
+ final int observerCount = mUserSwitchObservers.beginBroadcast();
+ for (int i = 0; i < observerCount; i++) {
+ try {
+ mUserSwitchObservers.getBroadcastItem(i).onForegroundProfileSwitch(userId);
+ } catch (RemoteException e) {
+ // Ignore
+ }
+ }
+ mUserSwitchObservers.finishBroadcast();
+ }
+
+ /** Called on handler thread */
+ void dispatchUserSwitchComplete(int userId) {
+ final int observerCount = mUserSwitchObservers.beginBroadcast();
+ for (int i = 0; i < observerCount; i++) {
+ try {
+ mUserSwitchObservers.getBroadcastItem(i).onUserSwitchComplete(userId);
+ } catch (RemoteException e) {
+ }
+ }
+ mUserSwitchObservers.finishBroadcast();
+ }
+
+ void timeoutUserSwitch(UserState uss, int oldUserId, int newUserId) {
+ synchronized (mService) {
+ Slog.w(TAG, "User switch timeout: from " + oldUserId + " to " + newUserId);
+ sendContinueUserSwitchLocked(uss, oldUserId, newUserId);
+ }
+ }
+
+ void dispatchUserSwitch(final UserState uss, final int oldUserId,
+ final int newUserId) {
+ final int observerCount = mUserSwitchObservers.beginBroadcast();
+ if (observerCount > 0) {
+ final IRemoteCallback callback = new IRemoteCallback.Stub() {
+ int mCount = 0;
+ @Override
+ public void sendResult(Bundle data) throws RemoteException {
+ synchronized (mService) {
+ if (mCurUserSwitchCallback == this) {
+ mCount++;
+ if (mCount == observerCount) {
+ sendContinueUserSwitchLocked(uss, oldUserId, newUserId);
+ }
+ }
+ }
+ }
+ };
+ synchronized (mService) {
+ uss.switching = true;
+ mCurUserSwitchCallback = callback;
+ }
+ for (int i = 0; i < observerCount; i++) {
+ try {
+ mUserSwitchObservers.getBroadcastItem(i).onUserSwitching(
+ newUserId, callback);
+ } catch (RemoteException e) {
+ }
+ }
+ } else {
+ synchronized (mService) {
+ sendContinueUserSwitchLocked(uss, oldUserId, newUserId);
+ }
+ }
+ mUserSwitchObservers.finishBroadcast();
+ }
+
+ void sendContinueUserSwitchLocked(UserState uss, int oldUserId, int newUserId) {
+ mCurUserSwitchCallback = null;
+ mHandler.removeMessages(USER_SWITCH_TIMEOUT_MSG);
+ mHandler.sendMessage(mHandler.obtainMessage(ActivityManagerService.CONTINUE_USER_SWITCH_MSG,
+ oldUserId, newUserId, uss));
+ }
+
+ void continueUserSwitch(UserState uss, int oldUserId, int newUserId) {
+ completeSwitchAndInitialize(uss, newUserId, false, true);
+ }
+
+ void onUserInitialized(UserState uss, boolean foreground, int oldUserId, int newUserId) {
+ synchronized (mService) {
+ if (foreground) {
+ moveUserToForegroundLocked(uss, oldUserId, newUserId);
+ }
+ }
+ completeSwitchAndInitialize(uss, newUserId, true, false);
+ }
+
+ void completeSwitchAndInitialize(UserState uss, int newUserId,
+ boolean clearInitializing, boolean clearSwitching) {
+ boolean unfrozen = false;
+ synchronized (mService) {
+ if (clearInitializing) {
+ uss.initializing = false;
+ getUserManagerLocked().makeInitialized(uss.mHandle.getIdentifier());
+ }
+ if (clearSwitching) {
+ uss.switching = false;
+ }
+ if (!uss.switching && !uss.initializing) {
+ mService.mWindowManager.stopFreezingScreen();
+ unfrozen = true;
+ }
+ }
+ if (unfrozen) {
+ mHandler.removeMessages(REPORT_USER_SWITCH_COMPLETE_MSG);
+ mHandler.sendMessage(mHandler.obtainMessage(REPORT_USER_SWITCH_COMPLETE_MSG,
+ newUserId, 0));
+ }
+ stopGuestUserIfBackground();
+ }
+
+ void moveUserToForegroundLocked(UserState uss, int oldUserId, int newUserId) {
+ boolean homeInFront = mService.mStackSupervisor.switchUserLocked(newUserId, uss);
+ if (homeInFront) {
+ mService.startHomeActivityLocked(newUserId, "moveUserToForeground");
+ } else {
+ mService.mStackSupervisor.resumeTopActivitiesLocked();
+ }
+ EventLogTags.writeAmSwitchUser(newUserId);
+ getUserManagerLocked().onUserForeground(newUserId);
+ mService.sendUserSwitchBroadcastsLocked(oldUserId, newUserId);
+ }
+
+ void registerUserSwitchObserver(IUserSwitchObserver observer) {
+ if (mService.checkCallingPermission(INTERACT_ACROSS_USERS_FULL)
+ != PackageManager.PERMISSION_GRANTED) {
+ final String msg = "Permission Denial: registerUserSwitchObserver() from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid()
+ + " requires " + INTERACT_ACROSS_USERS_FULL;
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+
+ mUserSwitchObservers.register(observer);
+ }
+
+ void unregisterUserSwitchObserver(IUserSwitchObserver observer) {
+ mUserSwitchObservers.unregister(observer);
+ }
+
+ UserState getStartedUserState(int userId) {
+ return mStartedUsers.get(userId);
+ }
+
+ boolean hasStartedUserState(int userId) {
+ return mStartedUsers.get(userId) != null;
+ }
+
+ private void updateStartedUserArrayLocked() {
+ int num = 0;
+ for (int i = 0; i < mStartedUsers.size(); i++) {
+ UserState uss = mStartedUsers.valueAt(i);
+ // This list does not include stopping users.
+ if (uss.mState != UserState.STATE_STOPPING
+ && uss.mState != UserState.STATE_SHUTDOWN) {
+ num++;
+ }
+ }
+ mStartedUserArray = new int[num];
+ num = 0;
+ for (int i = 0; i < mStartedUsers.size(); i++) {
+ UserState uss = mStartedUsers.valueAt(i);
+ if (uss.mState != UserState.STATE_STOPPING
+ && uss.mState != UserState.STATE_SHUTDOWN) {
+ mStartedUserArray[num] = mStartedUsers.keyAt(i);
+ num++;
+ }
+ }
+ }
+
+ void sendBootCompletedLocked(IIntentReceiver resultTo) {
+ for (int i = 0; i < mStartedUsers.size(); i++) {
+ UserState uss = mStartedUsers.valueAt(i);
+ if (uss.mState == UserState.STATE_BOOTING) {
+ uss.mState = UserState.STATE_RUNNING;
+ final int userId = mStartedUsers.keyAt(i);
+ Intent intent = new Intent(Intent.ACTION_BOOT_COMPLETED, null);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT);
+ mService.broadcastIntentLocked(null, null, intent, null,
+ resultTo, 0, null, null,
+ new String[]{android.Manifest.permission.RECEIVE_BOOT_COMPLETED},
+ AppOpsManager.OP_NONE, null, true, false,
+ ActivityManagerService.MY_PID, Process.SYSTEM_UID, userId);
+ }
+ }
+ }
+
+ /**
+ * Refreshes the list of users related to the current user when either a
+ * user switch happens or when a new related user is started in the
+ * background.
+ */
+ void updateCurrentProfileIdsLocked() {
+ final List<UserInfo> profiles = getUserManagerLocked().getProfiles(mCurrentUserId,
+ false /* enabledOnly */);
+ int[] currentProfileIds = new int[profiles.size()]; // profiles will not be null
+ for (int i = 0; i < currentProfileIds.length; i++) {
+ currentProfileIds[i] = profiles.get(i).id;
+ }
+ mCurrentProfileIds = currentProfileIds;
+
+ synchronized (mUserProfileGroupIdsSelfLocked) {
+ mUserProfileGroupIdsSelfLocked.clear();
+ final List<UserInfo> users = getUserManagerLocked().getUsers(false);
+ for (int i = 0; i < users.size(); i++) {
+ UserInfo user = users.get(i);
+ if (user.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID) {
+ mUserProfileGroupIdsSelfLocked.put(user.id, user.profileGroupId);
+ }
+ }
+ }
+ }
+
+ int[] getStartedUserArrayLocked() {
+ return mStartedUserArray;
+ }
+
+ UserInfo getCurrentUser() {
+ if ((mService.checkCallingPermission(INTERACT_ACROSS_USERS)
+ != PackageManager.PERMISSION_GRANTED) && (
+ mService.checkCallingPermission(INTERACT_ACROSS_USERS_FULL)
+ != PackageManager.PERMISSION_GRANTED)) {
+ String msg = "Permission Denial: getCurrentUser() from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid()
+ + " requires " + INTERACT_ACROSS_USERS;
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+ synchronized (mService) {
+ return getCurrentUserLocked();
+ }
+ }
+
+ UserInfo getCurrentUserLocked() {
+ int userId = mTargetUserId != UserHandle.USER_NULL ? mTargetUserId : mCurrentUserId;
+ return getUserManagerLocked().getUserInfo(userId);
+ }
+
+ int getCurrentUserIdLocked() {
+ return mTargetUserId != UserHandle.USER_NULL ? mTargetUserId : mCurrentUserId;
+ }
+
+ boolean isSameProfileGroup(int callingUserId, int targetUserId) {
+ synchronized (mUserProfileGroupIdsSelfLocked) {
+ int callingProfile = mUserProfileGroupIdsSelfLocked.get(callingUserId,
+ UserInfo.NO_PROFILE_GROUP_ID);
+ int targetProfile = mUserProfileGroupIdsSelfLocked.get(targetUserId,
+ UserInfo.NO_PROFILE_GROUP_ID);
+ return callingProfile != UserInfo.NO_PROFILE_GROUP_ID
+ && callingProfile == targetProfile;
+ }
+ }
+
+ boolean isCurrentProfileLocked(int userId) {
+ return ArrayUtils.contains(mCurrentProfileIds, userId);
+ }
+
+ void dump(PrintWriter pw, boolean dumpAll) {
+ pw.println(" mStartedUsers:");
+ for (int i = 0; i < mStartedUsers.size(); i++) {
+ UserState uss = mStartedUsers.valueAt(i);
+ pw.print(" User #"); pw.print(uss.mHandle.getIdentifier());
+ pw.print(": "); uss.dump("", pw);
+ }
+ pw.print(" mStartedUserArray: [");
+ for (int i = 0; i < mStartedUserArray.length; i++) {
+ if (i > 0) pw.print(", ");
+ pw.print(mStartedUserArray[i]);
+ }
+ pw.println("]");
+ pw.print(" mUserLru: [");
+ for (int i = 0; i < mUserLru.size(); i++) {
+ if (i > 0) pw.print(", ");
+ pw.print(mUserLru.get(i));
+ }
+ pw.println("]");
+ if (dumpAll) {
+ pw.print(" mStartedUserArray: "); pw.println(Arrays.toString(mStartedUserArray));
+ }
+ synchronized (mUserProfileGroupIdsSelfLocked) {
+ if (mUserProfileGroupIdsSelfLocked.size() > 0) {
+ pw.println(" mUserProfileGroupIds:");
+ for (int i=0; i<mUserProfileGroupIdsSelfLocked.size(); i++) {
+ pw.print(" User #");
+ pw.print(mUserProfileGroupIdsSelfLocked.keyAt(i));
+ pw.print(" -> profile #");
+ pw.println(mUserProfileGroupIdsSelfLocked.valueAt(i));
+ }
+ }
+ }
+ }
+
+}