From 0429f3522bca5bb86378dd3f013f995484ddbed6 Mon Sep 17 00:00:00 2001 From: Jorim Jaggi Date: Tue, 22 Dec 2015 16:29:16 +0100 Subject: [PATCH] Freeze task bounds when relaunching To make sure that task is only laid out with the size that matches the current configuration, we have to "freeze" the task bounds when we send a configuration change. Without this change, it could happen that the app already laid out with the new task bounds, but still had the old configuration, leading to wrong layouts. Bug: 26311778 Bug: 25015474 Change-Id: I8d3a3fdf3735f446a4affbbdb4986dafc97623a5 --- .../android/server/am/ActivityStackSupervisor.java | 3 +++ .../java/com/android/server/wm/AppWindowToken.java | 24 +++++++++++++++++++ services/core/java/com/android/server/wm/Task.java | 13 ++++++++-- .../core/java/com/android/server/wm/TaskStack.java | 7 ++++++ .../android/server/wm/WindowManagerService.java | 28 ++++++++++++++++++---- .../java/com/android/server/wm/WindowState.java | 16 ++++++++++++- 6 files changed, 83 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 0b22bd4edadc..80a75ce74312 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -1868,6 +1868,9 @@ public final class ActivityStackSupervisor implements DisplayListener { mTmpInsetBounds.put(task.taskId, tempTaskInsetBounds); } } + + // We might trigger a configuration change. Save the current task bounds for freezing. + mWindowManager.prepareFreezingTaskBounds(stack.mStackId); stack.mFullscreen = mWindowManager.resizeStack(stack.mStackId, bounds, mTmpConfigs, mTmpBounds, mTmpInsetBounds); stack.setBounds(bounds); diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index 9b9f14bee982..573aaec034cf 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -31,6 +31,7 @@ import com.android.server.wm.WindowManagerService.H; import android.annotation.NonNull; import android.content.pm.ActivityInfo; +import android.graphics.Rect; import android.os.Message; import android.os.RemoteException; import android.util.Slog; @@ -39,6 +40,7 @@ import android.view.View; import android.view.WindowManager; import java.io.PrintWriter; +import java.util.ArrayDeque; import java.util.ArrayList; class AppTokenList extends ArrayList { @@ -126,6 +128,8 @@ class AppWindowToken extends WindowToken { boolean mAlwaysFocusable; + ArrayDeque mFrozenBounds = new ArrayDeque<>(); + AppWindowToken(WindowManagerService _service, IApplicationToken _token, boolean _voiceInteraction) { super(_service, _token.asBinder(), @@ -437,6 +441,23 @@ class AppWindowToken extends WindowToken { } } + /** + * Freezes the task bounds. The size of this task reported the app will be fixed to the bounds + * freezed by {@link Task#prepareFreezingBounds} until {@link #unfreezeBounds} gets called, even + * if they change in the meantime. If the bounds are already frozen, the bounds will be frozen + * with a queue. + */ + void freezeBounds() { + mFrozenBounds.offer(new Rect(mTask.mPreparedFrozenBounds)); + } + + /** + * Unfreezes the previously frozen bounds. See {@link #freezeBounds}. + */ + void unfreezeBounds() { + mFrozenBounds.remove(); + } + @Override void dump(PrintWriter pw, String prefix) { super.dump(pw, prefix); @@ -483,6 +504,9 @@ class AppWindowToken extends WindowToken { pw.print(" startingDisplayed="); pw.print(startingDisplayed); pw.print(" startingMoved"); pw.println(startingMoved); } + if (!mFrozenBounds.isEmpty()) { + pw.print(prefix); pw.print("mFrozenBounds="); pw.print(mFrozenBounds); + } } @Override diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 6e65ac19589c..223e03ad4cc4 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -61,6 +61,7 @@ class Task implements DimLayer.DimLayerUser { // Content limits relative to the DisplayContent this sits in. private Rect mBounds = new Rect(); + final Rect mPreparedFrozenBounds = new Rect(); // Bounds used to calculate the insets. private final Rect mTempInsetBounds = new Rect(); @@ -200,8 +201,7 @@ class Task implements DimLayer.DimLayerUser { boolean removeAppToken(AppWindowToken wtoken) { boolean removed = mAppTokens.remove(wtoken); if (mAppTokens.size() == 0) { - EventLog.writeEvent(EventLogTags.WM_TASK_REMOVED, mTaskId, - "removeAppToken: last token"); + EventLog.writeEvent(EventLogTags.WM_TASK_REMOVED, mTaskId, "removeAppToken: last token"); if (mDeferRemoval) { removeLocked(); } @@ -314,6 +314,14 @@ class Task implements DimLayer.DimLayerUser { return true; } + /** + * Prepares the task bounds to be frozen with the current size. See + * {@link AppWindowToken#freezeBounds}. + */ + void prepareFreezingBounds() { + mPreparedFrozenBounds.set(mBounds); + } + boolean scrollLocked(Rect bounds) { // shift the task bound if it doesn't fully cover the stack area mStack.getDimBounds(mTmpRect); @@ -601,5 +609,6 @@ class Task implements DimLayer.DimLayerUser { pw.print(prefix + prefix); pw.print("mBounds="); pw.println(mBounds.toShortString()); pw.print(prefix + prefix); pw.print("mdr="); pw.println(mDeferRemoval); pw.print(prefix + prefix); pw.print("appTokens="); pw.println(mAppTokens); + pw.print(prefix + prefix); pw.print("mTempInsetBounds="); pw.println(mTempInsetBounds); } } diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java index e481d116a69f..67debe6e3773 100644 --- a/services/core/java/com/android/server/wm/TaskStack.java +++ b/services/core/java/com/android/server/wm/TaskStack.java @@ -148,6 +148,13 @@ public class TaskStack implements DimLayer.DimLayerUser { return true; } + void prepareFreezingTaskBounds() { + for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) { + final Task task = mTasks.get(taskNdx); + task.prepareFreezingBounds(); + } + } + boolean isFullscreenBounds(Rect bounds) { if (mDisplayContent == null || bounds == null) { return true; diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index ccde6061d6d6..be753354a6f8 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -203,7 +203,6 @@ import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT_METHOD; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_KEYGUARD; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_RESIZE; @@ -4843,6 +4842,17 @@ public class WindowManagerService extends IWindowManager.Stub } } + public void prepareFreezingTaskBounds(int stackId) { + synchronized (mWindowMap) { + final TaskStack stack = mStackIdToStack.get(stackId); + if (stack == null) { + throw new IllegalArgumentException("prepareFreezingTaskBounds: stackId " + stackId + + " not found."); + } + stack.prepareFreezingTaskBounds(); + } + } + public void positionTaskInStack(int taskId, int stackId, int position, Rect bounds, Configuration config) { synchronized (mWindowMap) { @@ -9472,8 +9482,8 @@ public class WindowManagerService extends IWindowManager.Stub public void notifyAppRelaunching(IBinder token) { synchronized (mWindowMap) { AppWindowToken appWindow = findAppWindowToken(token); - if (appWindow != null) { - // TODO: Do something useful + if (canFreezeBounds(appWindow)) { + appWindow.freezeBounds(); } } } @@ -9481,12 +9491,20 @@ public class WindowManagerService extends IWindowManager.Stub public void notifyAppRelaunchingFinished(IBinder token) { synchronized (mWindowMap) { AppWindowToken appWindow = findAppWindowToken(token); - if (appWindow != null) { - // TODO: Do something useful + if (canFreezeBounds(appWindow)) { + appWindow.unfreezeBounds(); } } } + private boolean canFreezeBounds(AppWindowToken appWindow) { + + // For freeform windows, we can't freeze the bounds at the moment because this would make + // the resizing unresponsive. + return appWindow != null && appWindow.mTask != null + && !appWindow.mTask.inFreeformWorkspace(); + } + void dumpPolicyLocked(PrintWriter pw, String[] args, boolean dumpAll) { pw.println("WINDOW MANAGER POLICY STATE (dumpsys window policy)"); mPolicy.dump(" ", pw, args); diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 0eb1c6e7d9d9..e51080e7f230 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -624,6 +624,14 @@ final class WindowState implements WindowManagerPolicy.WindowState { } else { task.getBounds(mContainingFrame); task.getTempInsetBounds(mInsetFrame); + if (mAppToken != null && !mAppToken.mFrozenBounds.isEmpty()) { + + // If the bounds are frozen, we still want to translate the window freely and only + // freeze the size. + Rect frozen = mAppToken.mFrozenBounds.peek(); + mContainingFrame.right = mContainingFrame.left + frozen.width(); + mContainingFrame.bottom = mContainingFrame.top + frozen.height(); + } final WindowState imeWin = mService.mInputMethodWindow; if (imeWin != null && imeWin.isVisibleNow() && mService.mInputMethodTarget == this && mContainingFrame.bottom > cf.bottom) { @@ -2029,7 +2037,13 @@ final class WindowState implements WindowManagerPolicy.WindowState { if (task.isDragResizing()) { return true; } - return mDisplayContent.mDividerControllerLocked.isResizing() && + + // If the bounds are currently frozen, it means that the layout size that the app sees + // and the bounds we clip this window to might be different. In order to avoid holes, we + // simulate that we are still resizing so the app fills the hole with the resizing + // background. + return (mDisplayContent.mDividerControllerLocked.isResizing() + || mAppToken != null && !mAppToken.mFrozenBounds.isEmpty()) && !task.inFreeformWorkspace() && !task.isFullscreen(); } -- 2.11.0