Fix user switching.
- Save and restore WindowManager stack states.
- Maintain ActivityManager activity states based on the stack
the activity is in.
Fixes bug 8646641.
Change-Id: I16c76c7708ab49121c3884a7e5bf219898b92d3f
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index dd113c3..c750cad 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -3612,7 +3612,7 @@
"for process:"+packageName);
}
}
-
+
try {
// Clear application user data
pm.clearApplicationUserData(packageName, observer, userId);
@@ -3681,7 +3681,7 @@
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
-
+
long callingId = Binder.clearCallingIdentity();
try {
synchronized(this) {
@@ -3702,7 +3702,7 @@
}
}
}
-
+
int N = procs.size();
for (int i=0; i<N; i++) {
removeProcessLocked(procs.get(i), false, true, "kill all background");
@@ -3971,7 +3971,7 @@
procs.add(app);
}
}
-
+
int N = procs.size();
for (int i=0; i<N; i++) {
removeProcessLocked(procs.get(i), callerWillRestart, allowRestart, reason);
@@ -4133,7 +4133,7 @@
}
}
if (mBooted) {
- mStackSupervisor.resumeTopActivityLocked();
+ mStackSupervisor.resumeTopActivitiesLocked();
mStackSupervisor.scheduleIdleLocked();
}
}
@@ -4983,7 +4983,8 @@
updateOomAdjLocked();
}
}
-
+
+ @Override
public void setProcessForeground(IBinder token, int pid, boolean isForeground) {
enforceCallingPermission(android.Manifest.permission.SET_PROCESS_LIMIT,
"setProcessForeground()");
@@ -5007,6 +5008,7 @@
}
if (isForeground && token != null) {
ForegroundToken newToken = new ForegroundToken() {
+ @Override
public void binderDied() {
foregroundTokenDied(this);
}
@@ -5041,6 +5043,7 @@
mActivityManagerService = activityManagerService;
}
+ @Override
public boolean checkPermission(String permission, int pid, int uid) {
return mActivityManagerService.checkPermission(permission, pid,
uid) == PackageManager.PERMISSION_GRANTED;
@@ -5048,12 +5051,14 @@
}
class IntentFirewallInterface implements IntentFirewall.AMSInterface {
+ @Override
public int checkComponentPermission(String permission, int pid, int uid,
int owningUid, boolean exported) {
return ActivityManagerService.this.checkComponentPermission(permission, pid, uid,
owningUid, exported);
}
+ @Override
public Object getAMSLock() {
return ActivityManagerService.this;
}
@@ -8281,7 +8286,7 @@
} finally {
Binder.restoreCallingIdentity(ident);
}
- mStackSupervisor.resumeTopActivityLocked();
+ mStackSupervisor.resumeTopActivitiesLocked();
sendUserSwitchBroadcastsLocked(-1, mCurrentUserId);
}
}
@@ -8396,10 +8401,10 @@
// annoy the user repeatedly. Unless it is persistent, since those
// processes run critical code.
removeProcessLocked(app, false, false, "crash");
- mStackSupervisor.resumeTopActivityLocked();
+ mStackSupervisor.resumeTopActivitiesLocked();
return false;
}
- mStackSupervisor.resumeTopActivityLocked();
+ mStackSupervisor.resumeTopActivitiesLocked();
} else {
mStackSupervisor.finishTopRunningActivityLocked(app);
}
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index 3557e90..3758bb1 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -234,7 +234,7 @@
int mThumbnailWidth = -1;
int mThumbnailHeight = -1;
- private int mCurrentUser;
+ int mCurrentUser;
final int mStackId;
@@ -576,7 +576,8 @@
}
/*
- * Move the activities around in the stack to bring a user to the foreground.
+ * Move the activities around in the stack to bring a user to the foreground. This only
+ * matters on the home stack. All other stacks are single user.
* @return whether there are any activities for the specified user.
*/
final boolean switchUserLocked(int userId, UserStartedState uss) {
@@ -602,9 +603,6 @@
}
}
- // task is now the original topmost TaskRecord. Transition from the old top to the new top.
- ActivityRecord top = task != null ? task.getTopActivity() : null;
- resumeTopActivityLocked(top);
return haveActivities;
}
@@ -1718,7 +1716,7 @@
}
if (doResume) {
- mStackSupervisor.resumeTopActivityLocked();
+ mStackSupervisor.resumeTopActivitiesLocked();
}
}
diff --git a/services/java/com/android/server/am/ActivityStackSupervisor.java b/services/java/com/android/server/am/ActivityStackSupervisor.java
index 89b0ff2..528bf6f 100644
--- a/services/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/java/com/android/server/am/ActivityStackSupervisor.java
@@ -61,6 +61,7 @@
import android.os.UserHandle;
import android.util.EventLog;
import android.util.Slog;
+import android.util.SparseArray;
import com.android.internal.app.HeavyWeightSwitcherActivity;
import com.android.server.am.ActivityManagerService.PendingActivityLaunch;
@@ -160,6 +161,9 @@
* is being brought in front of us. */
boolean mUserLeaving = false;
+ /** Stacks belonging to users other than mCurrentUser. Indexed by userId. */
+ final SparseArray<UserState> mUserStates = new SparseArray<UserState>();
+
public ActivityStackSupervisor(ActivityManagerService service, Context context,
Looper looper) {
mService = service;
@@ -213,6 +217,9 @@
}
boolean isFrontStack(ActivityStack stack) {
+ if (stack.mCurrentUser != mCurrentUser) {
+ return false;
+ }
return !(stack.isHomeStack() ^ getFocusedStack().isHomeStack());
}
@@ -319,6 +326,9 @@
final String processName = app.processName;
for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = mStacks.get(stackNdx);
+ if (!isFrontStack(stack)) {
+ continue;
+ }
ActivityRecord hr = stack.topRunningActivityLocked(null);
if (hr != null) {
if (hr.app == null && app.uid == hr.info.applicationInfo.uid
@@ -412,7 +422,7 @@
}
void reportActivityVisibleLocked(ActivityRecord r) {
- for (int i=mWaitingActivityVisible.size()-1; i>=0; i--) {
+ for (int i = mWaitingActivityVisible.size()-1; i >= 0; i--) {
WaitResult w = mWaitingActivityVisible.get(i);
w.timeout = false;
if (r != null) {
@@ -449,6 +459,9 @@
}
for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = mStacks.get(stackNdx);
+ if (stack.mCurrentUser != mCurrentUser) {
+ continue;
+ }
if (stack != mFocusedStack && isFrontStack(stack)) {
r = stack.topRunningActivityLocked(null);
if (r != null) {
@@ -1145,8 +1158,14 @@
ActivityStack getCorrectStack(ActivityRecord r) {
if (!r.isHomeActivity) {
- if (mStacks.size() == 1) {
- // Time to create the first app stack.
+ int stackNdx;
+ for (stackNdx = mStacks.size() - 1; stackNdx > 0; --stackNdx) {
+ if (mStacks.get(stackNdx).mCurrentUser == mCurrentUser) {
+ break;
+ }
+ }
+ if (stackNdx == 0) {
+ // Time to create the first app stack for this user.
int stackId = mService.createStack(-1, HOME_STACK_ID,
StackBox.TASK_STACK_GOES_OVER, 1.0f);
mFocusedStack = getStack(stackId);
@@ -1776,7 +1795,7 @@
return didSomething;
}
- void resumeTopActivityLocked() {
+ void resumeTopActivitiesLocked() {
for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = mStacks.get(stackNdx);
if (isFrontStack(stack)) {
@@ -1816,18 +1835,16 @@
}
int createStack() {
- synchronized (this) {
- while (true) {
- if (++mLastStackId <= HOME_STACK_ID) {
- mLastStackId = HOME_STACK_ID + 1;
- }
- if (getStack(mLastStackId) == null) {
- break;
- }
+ while (true) {
+ if (++mLastStackId <= HOME_STACK_ID) {
+ mLastStackId = HOME_STACK_ID + 1;
}
- mStacks.add(new ActivityStack(mService, mContext, mLooper, mLastStackId));
- return mLastStackId;
+ if (getStack(mLastStackId) == null) {
+ break;
+ }
}
+ mStacks.add(new ActivityStack(mService, mContext, mLooper, mLastStackId));
+ return mLastStackId;
}
void moveTaskToStack(int taskId, int stackId, boolean toTop) {
@@ -1938,16 +1955,22 @@
}
boolean switchUserLocked(int userId, UserStartedState uss) {
+ mUserStates.put(mCurrentUser, new UserState());
mCurrentUser = userId;
- mStartingUsers.add(uss);
- boolean haveActivities = false;
- final int numStacks = mStacks.size();
- for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
- final ActivityStack stack = mStacks.get(stackNdx);
- if (isFrontStack(stack)) {
- haveActivities |= stack.switchUserLocked(userId, uss);
- }
+ UserState userState = mUserStates.get(userId);
+ if (userState != null) {
+ userState.restore();
+ mUserStates.delete(userId);
+ } else {
+ mFocusedStack = null;
+ mStackState = STACK_STATE_HOME_IN_FRONT;
}
+
+ mStartingUsers.add(uss);
+ boolean haveActivities = mHomeStack.switchUserLocked(userId, uss);
+
+ resumeTopActivitiesLocked();
+
return haveActivities;
}
@@ -2203,4 +2226,21 @@
}
}
}
+
+ private final class UserState {
+ final ActivityStack mSavedFocusedStack;
+ final int mSavedStackState;
+
+ public UserState() {
+ ActivityStackSupervisor supervisor = ActivityStackSupervisor.this;
+ mSavedFocusedStack = supervisor.mFocusedStack;
+ mSavedStackState = supervisor.mStackState;
+ }
+
+ void restore() {
+ ActivityStackSupervisor supervisor = ActivityStackSupervisor.this;
+ supervisor.mFocusedStack = mSavedFocusedStack;
+ supervisor.mStackState = mSavedStackState;
+ }
+ }
}
diff --git a/services/java/com/android/server/wm/DisplayContent.java b/services/java/com/android/server/wm/DisplayContent.java
index 8343986..45f018d 100644
--- a/services/java/com/android/server/wm/DisplayContent.java
+++ b/services/java/com/android/server/wm/DisplayContent.java
@@ -18,10 +18,12 @@
import static com.android.server.am.ActivityStackSupervisor.HOME_STACK_ID;
import static com.android.server.wm.WindowManagerService.DEBUG_STACK;
+import static com.android.server.wm.WindowManagerService.DEBUG_VISIBILITY;
import static com.android.server.wm.WindowManagerService.TAG;
import android.graphics.Rect;
import android.util.Slog;
+import android.util.SparseArray;
import android.view.Display;
import android.view.DisplayInfo;
import android.view.InputChannel;
@@ -104,6 +106,8 @@
/** Detect user tapping outside of current focused stack bounds .*/
StackTapDetector mTapDetector;
+ SparseArray<UserStacks> mUserStacks = new SparseArray<UserStacks>();
+
/**
* @param display May not be null.
*/
@@ -145,6 +149,22 @@
*/
ArrayList<Task> getTasks() {
mTmpTasks.clear();
+ // First do the tasks belonging to other users.
+ final int numUserStacks = mUserStacks.size();
+ for (int i = 0; i < numUserStacks; ++i) {
+ UserStacks userStacks = mUserStacks.valueAt(i);
+ ArrayList<TaskStack> stacks = userStacks.mSavedStackHistory;
+ final int numStacks = stacks.size();
+ for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
+ TaskStack stack = stacks.get(stackNdx);
+ if (stack != mHomeStack) {
+ if (WindowManagerService.DEBUG_LAYERS) Slog.i(TAG, "getTasks: mStackHistory=" +
+ mStackHistory);
+ mTmpTasks.addAll(stack.getTasks());
+ }
+ }
+ }
+ // Now do the current user's tasks.
final int numStacks = mStackHistory.size();
for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
mTmpTasks.addAll(mStackHistory.get(stackNdx).getTasks());
@@ -292,6 +312,14 @@
return bounds;
}
}
+ // Not in the visible stacks, try the saved ones.
+ for (int userNdx = mUserStacks.size() - 1; userNdx >= 0; --userNdx) {
+ UserStacks userStacks = mUserStacks.valueAt(userNdx);
+ Rect bounds = userStacks.mSavedStackBox.getStackBounds(stackId);
+ if (bounds != null) {
+ return bounds;
+ }
+ }
return null;
}
@@ -300,6 +328,26 @@
return topBox.stackIdFromPoint(x, y);
}
+ void switchUserStacks(int oldUserId, int newUserId) {
+ final WindowList windows = getWindowList();
+ for (int i = 0; i < windows.size(); i++) {
+ final WindowState win = windows.get(i);
+ if (win.isHiddenFromUserLocked()) {
+ if (DEBUG_VISIBILITY) Slog.w(TAG, "user changing " + newUserId + " hiding "
+ + win + ", attrs=" + win.mAttrs.type + ", belonging to "
+ + win.mOwnerUid);
+ win.hideLw(false);
+ }
+ }
+ // Clear the old user's non-home StackBox
+ mUserStacks.put(oldUserId, new UserStacks());
+ UserStacks userStacks = mUserStacks.get(newUserId);
+ if (userStacks != null) {
+ userStacks.restore();
+ mUserStacks.delete(newUserId);
+ }
+ }
+
public void dump(String prefix, PrintWriter pw) {
pw.print(prefix); pw.print("Display: mDisplayId="); pw.println(mDisplayId);
final String subPrefix = " " + prefix;
@@ -365,6 +413,49 @@
token.dump(pw, " ");
}
}
+ if (mUserStacks.size() > 0) {
+ pw.println();
+ pw.println(" Saved user stacks:");
+ for (int i = 0; i < mUserStacks.size(); ++i) {
+ UserStacks userStacks = mUserStacks.valueAt(i);
+ pw.print(" UserId="); pw.println(Integer.toHexString(mUserStacks.keyAt(i)));
+ pw.print(" StackHistory="); pw.println(userStacks.mSavedStackHistory);
+ pw.print(" StackBox="); userStacks.mSavedStackBox.dump(" ", pw);
+ }
+ }
pw.println();
}
+
+ private final class UserStacks {
+ final ArrayList<TaskStack> mSavedStackHistory;
+ StackBox mSavedStackBox;
+ int mBoxNdx;
+
+ public UserStacks() {
+ mSavedStackHistory = new ArrayList<TaskStack>(mStackHistory);
+ for (int stackNdx = mStackHistory.size() - 1; stackNdx >=0; --stackNdx) {
+ if (mStackHistory.get(stackNdx) != mHomeStack) {
+ mStackHistory.remove(stackNdx);
+ }
+ }
+ mSavedStackBox = null;
+ mBoxNdx = -1;
+ for (int boxNdx = mStackBoxes.size() - 1; boxNdx >= 0; --boxNdx) {
+ StackBox box = mStackBoxes.get(boxNdx);
+ if (box.mStack != mHomeStack) {
+ mSavedStackBox = box;
+ mBoxNdx = boxNdx;
+ mStackBoxes.remove(boxNdx);
+ break;
+ }
+ }
+ }
+
+ void restore() {
+ mStackHistory = mSavedStackHistory;
+ if (mBoxNdx >= 0) {
+ mStackBoxes.add(mBoxNdx, mSavedStackBox);
+ }
+ }
+ }
}
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 5a9d63a..cb4333f 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -5094,22 +5094,16 @@
public void setCurrentUser(final int newUserId) {
synchronized (mWindowMap) {
+ int oldUserId = mCurrentUserId;
mCurrentUserId = newUserId;
mPolicy.setCurrentUserLw(newUserId);
// Hide windows that should not be seen by the new user.
DisplayContentsIterator iterator = new DisplayContentsIterator();
while (iterator.hasNext()) {
- final WindowList windows = iterator.next().getWindowList();
- for (int i = 0; i < windows.size(); i++) {
- final WindowState win = windows.get(i);
- if (win.isHiddenFromUserLocked()) {
- Slog.w(TAG, "current user violation " + newUserId + " hiding "
- + win + ", attrs=" + win.mAttrs.type + ", belonging to "
- + win.mOwnerUid);
- win.hideLw(false);
- }
- }
+ DisplayContent displayContent = iterator.next();
+ displayContent.switchUserStacks(oldUserId, newUserId);
+ rebuildAppWindowListLocked(displayContent);
}
performLayoutAndPlaceSurfacesLocked();
}