diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 80a75ce743128..4672023a220e5 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -1958,10 +1958,10 @@ public final class ActivityStackSupervisor implements DisplayListener { || tempOtherTaskInsetBounds != null); } - void resizeTaskLocked(TaskRecord task, Rect bounds, int resizeMode, boolean preserveWindow) { + boolean resizeTaskLocked(TaskRecord task, Rect bounds, int resizeMode, boolean preserveWindow) { if (!task.mResizeable) { Slog.w(TAG, "resizeTask: task " + task + " not resizeable."); - return; + return true; } adjustForMinimalTaskDimensions(task, bounds); @@ -1971,7 +1971,7 @@ public final class ActivityStackSupervisor implements DisplayListener { final boolean forced = (resizeMode & RESIZE_MODE_FORCED) != 0; if (task.mBounds != null && task.mBounds.equals(bounds) && !forced) { // Nothing to do here... - return; + return true; } if (!mWindowManager.isValidTaskId(task.taskId)) { @@ -1983,7 +1983,7 @@ public final class ActivityStackSupervisor implements DisplayListener { // re-restore the task so it can have the proper stack association. restoreRecentTaskLocked(task, FREEFORM_WORKSPACE_STACK_ID); } - return; + return true; } // Do not move the task to another stack here. @@ -2012,6 +2012,7 @@ public final class ActivityStackSupervisor implements DisplayListener { mWindowManager.resizeTask(task.taskId, bounds, task.mOverrideConfig, kept, forced); Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); + return kept; } private void adjustForMinimalTaskDimensions(TaskRecord task, Rect bounds) { @@ -2169,13 +2170,17 @@ public final class ActivityStackSupervisor implements DisplayListener { } final ActivityRecord topActivity = task.getTopActivity(); - if (StackId.preserveWindowOnTaskMove(stackId) && topActivity != null) { + final boolean mightReplaceWindow = + StackId.preserveWindowOnTaskMove(stackId) && topActivity != null; + if (mightReplaceWindow) { // We are about to relaunch the activity because its configuration changed due to // being maximized, i.e. size change. The activity will first remove the old window // and then add a new one. This call will tell window manager about this, so it can // preserve the old window until the new one is drawn. This prevents having a gap // between the removal and addition, in which no window is visible. We also want the // entrance of the new window to be properly animated. + // Note here we always set the replacing window first, as the flags might be needed + // during the relaunch. If we end up not doing any relaunch, we clear the flags later. mWindowManager.setReplacingWindow(topActivity.appToken, animate); } final ActivityStack stack = moveTaskToStackUncheckedLocked( @@ -2185,15 +2190,23 @@ public final class ActivityStackSupervisor implements DisplayListener { stack.mNoAnimActivities.add(topActivity); } + boolean kept = true; // Make sure the task has the appropriate bounds/size for the stack it is in. if (stackId == FULLSCREEN_WORKSPACE_STACK_ID && task.mBounds != null) { - resizeTaskLocked(task, stack.mBounds, RESIZE_MODE_SYSTEM, !PRESERVE_WINDOWS); + kept = resizeTaskLocked(task, stack.mBounds, RESIZE_MODE_SYSTEM, !PRESERVE_WINDOWS); } else if (stackId == FREEFORM_WORKSPACE_STACK_ID && task.mBounds == null && task.mLastNonFullscreenBounds != null) { - resizeTaskLocked(task, task.mLastNonFullscreenBounds, + kept = resizeTaskLocked(task, task.mLastNonFullscreenBounds, RESIZE_MODE_SYSTEM, !PRESERVE_WINDOWS); } else if (stackId == DOCKED_STACK_ID || stackId == PINNED_STACK_ID) { - resizeTaskLocked(task, stack.mBounds, RESIZE_MODE_SYSTEM, !PRESERVE_WINDOWS); + kept = resizeTaskLocked(task, stack.mBounds, RESIZE_MODE_SYSTEM, !PRESERVE_WINDOWS); + } + + if (mightReplaceWindow) { + // If we didn't actual do a relaunch (indicated by kept==true meaning we kept the old + // window), we need to clear the replace window settings. Otherwise, we schedule a + // timeout to remove the old window if the replacing window is not coming in time. + mWindowManager.scheduleClearReplacingWindowIfNeeded(topActivity.appToken, !kept); } // The task might have already been running and its visibility needs to be synchronized with diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index 573aaec034cfd..751f8715a1e13 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -25,6 +25,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_MOVEMENT; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; +import static com.android.server.wm.WindowManagerService.WINDOW_REPLACEMENT_TIMEOUT_DURATION; import com.android.server.input.InputApplicationHandle; import com.android.server.wm.WindowManagerService.H; @@ -403,12 +404,28 @@ class AppWindowToken extends WindowToken { } } + void resetReplacingWindows() { + if (DEBUG_ADD_REMOVE) Slog.d(TAG_WM, "Resetting app token " + appWindowToken + + " of replacing window marks."); + + for (int i = allAppWindows.size() - 1; i >= 0; i--) { + final WindowState w = allAppWindows.get(i); + w.resetReplacing(); + } + } + void addWindow(WindowState w) { for (int i = allAppWindows.size() - 1; i >= 0; i--) { WindowState candidate = allAppWindows.get(i); if (candidate.mWillReplaceWindow && candidate.mReplacingWindow == null && candidate.getWindowTag().equals(w.getWindowTag().toString())) { candidate.mReplacingWindow = w; + + // if we got a replacement window, reset the timeout to give drawing more time + service.mH.removeMessages(H.WINDOW_REPLACEMENT_TIMEOUT); + service.mH.sendMessageDelayed( + service.mH.obtainMessage(H.WINDOW_REPLACEMENT_TIMEOUT, this), + WINDOW_REPLACEMENT_TIMEOUT_DURATION); } } allAppWindows.add(w); @@ -424,7 +441,7 @@ class AppWindowToken extends WindowToken { return false; } - void clearTimedoutReplaceesLocked() { + void clearTimedoutReplacesLocked() { for (int i = allAppWindows.size() - 1; i >= 0; // removeWindowLocked at bottom of loop may remove multiple entries from // allAppWindows if the window to be removed has child windows. It also may @@ -437,7 +454,11 @@ class AppWindowToken extends WindowToken { continue; } candidate.mWillReplaceWindow = false; - service.removeWindowLocked(candidate); + // Since the window already timed out, remove it immediately now. + // Use removeWindowInnerLocked() instead of removeWindowLocked(), as the latter + // delays removal on certain conditions, which will leave the stale window in the + // stack and marked mWillReplaceWindow=false, so the window will never be removed. + service.removeWindowInnerLocked(candidate); } } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index d722ec7a48033..213f14b8c6791 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -8106,7 +8106,7 @@ public class WindowManagerService extends IWindowManager.Stub case WINDOW_REPLACEMENT_TIMEOUT: { final AppWindowToken token = (AppWindowToken) msg.obj; synchronized (mWindowMap) { - token.clearTimedoutReplaceesLocked(); + token.clearTimedoutReplacesLocked(); } } break; @@ -10192,11 +10192,34 @@ public class WindowManagerService extends IWindowManager.Stub } appWindowToken.setReplacingWindows(animate); } + } - if (appWindowToken != null) { - mH.removeMessages(H.WINDOW_REPLACEMENT_TIMEOUT); - mH.sendMessageDelayed(mH.obtainMessage(H.WINDOW_REPLACEMENT_TIMEOUT, appWindowToken), - WINDOW_REPLACEMENT_TIMEOUT_DURATION); + /** + * If we're replacing the window, schedule a timer to clear the replaced window + * after a timeout, in case the replacing window is not coming. + * + * If we're not replacing the window, clear the replace window settings of the app. + * + * @param token Application token for the activity whose window might be replaced. + * @param replacing Whether the window is being replaced or not. + */ + public void scheduleClearReplacingWindowIfNeeded(IBinder token, boolean replacing) { + AppWindowToken appWindowToken = null; + synchronized (mWindowMap) { + appWindowToken = findAppWindowToken(token); + if (appWindowToken == null) { + Slog.w(TAG_WM, "Attempted to reset replacing window on non-existing app token " + + token); + return; + } + if (replacing) { + mH.removeMessages(H.WINDOW_REPLACEMENT_TIMEOUT); + mH.sendMessageDelayed( + mH.obtainMessage(H.WINDOW_REPLACEMENT_TIMEOUT, appWindowToken), + WINDOW_REPLACEMENT_TIMEOUT_DURATION); + } else { + appWindowToken.resetReplacingWindows(); + } } } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 6e4e01f38212f..b7fd60f1c201f 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -2363,4 +2363,10 @@ final class WindowState implements WindowManagerPolicy.WindowState { mReplacingWindow = null; mAnimateReplacingWindow = animate; } + + void resetReplacing() { + mWillReplaceWindow = false; + mReplacingWindow = null; + mAnimateReplacingWindow = false; + } }