From d9d35bdfcf0e18ae16c0f59290b860700fb6e62f Mon Sep 17 00:00:00 2001 From: Chong Zhang Date: Thu, 4 Aug 2016 17:55:21 -0700 Subject: [PATCH] Fix WM missing stack/task when activity is force stopped and restarted When activity process is force-stopped (like in the case we need to stop it to allow debugger), activity record is removed and a new instance is added. Depending on timing, adding of new record could happen before or after old record is removed. If it added after old record is removed, then the removal could cause AM to also remove the task AND the stack (if the activity is the last one). On WM side however, the task/stack removal could be delayed due to animation. So when AM attaches a new stack, we end up with two stacks with the same stack id in the DisplayContent. Also, AM reuses the last used taskId after it's deleted. So WM is confused and didn't add a new task record. Then when the delayed stack removal fires everything is gone. We need to use a different task id when creating a new task record, even if the last created task is already deleted. Also when attaching a new stack to WM, first check if we already have such a stack in DisplayContent, if so, reattach it and don't create a duplicate. bug: 30465601 Change-Id: Ic399a32c54f6acdcbb9a0c6155599331d55df232 --- .../com/android/server/am/ActivityStackSupervisor.java | 17 +++++++++++------ .../java/com/android/server/wm/DisplayContent.java | 10 ++++++++++ .../com/android/server/wm/WindowManagerService.java | 18 ++++++++++++++++-- 3 files changed, 37 insertions(+), 8 deletions(-) diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index e31df57f56d4..ece1ee95f320 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -781,20 +781,25 @@ public final class ActivityStackSupervisor implements DisplayListener { } } + static int nextTaskIdForUser(int taskId, int userId) { + int nextTaskId = taskId + 1; + if (nextTaskId == (userId + 1) * MAX_TASK_IDS_PER_USER) { + // Wrap around as there will be smaller task ids that are available now. + nextTaskId -= MAX_TASK_IDS_PER_USER; + } + return nextTaskId; + } + int getNextTaskIdForUserLocked(int userId) { final int currentTaskId = mCurTaskIdForUser.get(userId, userId * MAX_TASK_IDS_PER_USER); // for a userId u, a taskId can only be in the range // [u*MAX_TASK_IDS_PER_USER, (u+1)*MAX_TASK_IDS_PER_USER-1], so if MAX_TASK_IDS_PER_USER // was 10, user 0 could only have taskIds 0 to 9, user 1: 10 to 19, user 2: 20 to 29, so on. - int candidateTaskId = currentTaskId; + int candidateTaskId = nextTaskIdForUser(currentTaskId, userId); while (mRecentTasks.taskIdTakenForUserLocked(candidateTaskId, userId) || anyTaskForIdLocked(candidateTaskId, !RESTORE_FROM_RECENTS, INVALID_STACK_ID) != null) { - candidateTaskId++; - if (candidateTaskId == (userId + 1) * MAX_TASK_IDS_PER_USER) { - // Wrap around as there will be smaller task ids that are available now. - candidateTaskId -= MAX_TASK_IDS_PER_USER; - } + candidateTaskId = nextTaskIdForUser(candidateTaskId, userId); if (candidateTaskId == currentTaskId) { // Something wrong! // All MAX_TASK_IDS_PER_USER task ids are taken up by running tasks for this user diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index fba439f8ca60..1d57872b2fbc 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -191,6 +191,16 @@ class DisplayContent { return mHomeStack; } + TaskStack getStackById(int stackId) { + for (int i = mStacks.size() - 1; i >= 0; --i) { + final TaskStack stack = mStacks.get(i); + if (stack.mStackId == stackId) { + return stack; + } + } + return null; + } + void updateDisplayInfo() { mDisplay.getDisplayInfo(mDisplayInfo); mDisplay.getMetrics(mDisplayMetrics); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 6451c7492fa8..6a184b24a46b 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -5047,18 +5047,32 @@ public class WindowManagerService extends IWindowManager.Stub try { synchronized (mWindowMap) { final DisplayContent displayContent = mDisplayContents.get(displayId); + boolean attachedToDisplay = false; if (displayContent != null) { TaskStack stack = mStackIdToStack.get(stackId); if (stack == null) { if (DEBUG_STACK) Slog.d(TAG_WM, "attachStack: stackId=" + stackId); - stack = new TaskStack(this, stackId); + + stack = displayContent.getStackById(stackId); + if (stack != null) { + // It's already attached to the display. Detach and re-attach + // because onTop might change, and be sure to clear mDeferDetach! + displayContent.detachStack(stack); + stack.mDeferDetach = false; + attachedToDisplay = true; + } else { + stack = new TaskStack(this, stackId); + } + mStackIdToStack.put(stackId, stack); if (stackId == DOCKED_STACK_ID) { getDefaultDisplayContentLocked().mDividerControllerLocked .notifyDockedStackExistsChanged(true); } } - stack.attachDisplayContent(displayContent); + if (!attachedToDisplay) { + stack.attachDisplayContent(displayContent); + } displayContent.attachStack(stack, onTop); if (stack.getRawFullscreen()) { return null; -- 2.11.0