Move window replacement tracking to window state.
In preparation for supporting replacement of child windows
we make replacement per window rather than per app.
Bug: 26070641
Change-Id: Ifa332086599c125611e430219c9497bae7e2ce31
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index fd5c704..b49641f 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -19,6 +19,7 @@
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
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;
@@ -122,21 +123,6 @@
// True if the windows associated with this token should be cropped to their stack bounds.
boolean mCropWindowsToStack;
- // This application will have its window replaced due to relaunch. This allows window manager
- // to differentiate between simple removal of a window and replacement. In the latter case it
- // will preserve the old window until the new one is drawn.
- boolean mWillReplaceWindow;
- // If true, the replaced window was already requested to be removed.
- boolean mReplacingRemoveRequested;
- // Whether the replacement of the window should trigger app transition animation.
- boolean mAnimateReplacingWindow;
- // If not null, the window that will be used to replace the old one. This is being set when
- // the window is added and unset when this window reports its first draw.
- WindowState mReplacingWindow;
- // Whether the new window has replaced the old one, so the old one can be removed without
- // blinking.
- boolean mHasReplacedWindow;
-
AppWindowToken(WindowManagerService _service, IApplicationToken _token,
boolean _voiceInteraction) {
super(_service, _token.asBinder(),
@@ -392,6 +378,62 @@
}
}
+ void setReplacingWindows(boolean animate) {
+ if (DEBUG_ADD_REMOVE) Slog.d(TAG_WM, "Marking app token " + appWindowToken
+ + " with replacing windows.");
+
+ for (int i = allAppWindows.size() - 1; i >= 0; i--) {
+ final WindowState w = allAppWindows.get(i);
+ w.setReplacing(animate);
+ }
+ if (animate) {
+ // Set-up dummy animation so we can start treating windows associated with this
+ // token like they are in transition before the new app window is ready for us to
+ // run the real transition animation.
+ if (DEBUG_APP_TRANSITIONS) Slog.v(TAG_WM,
+ "setReplacingWindow() Setting dummy animation on: " + this);
+ mAppAnimator.setDummyAnimation();
+ }
+ }
+
+ 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;
+ }
+ }
+ allAppWindows.add(w);
+ }
+
+ boolean waitingForReplacement() {
+ for (int i = allAppWindows.size() -1; i >= 0; i--) {
+ WindowState candidate = allAppWindows.get(i);
+ if (candidate.mWillReplaceWindow) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void clearTimedoutReplaceesLocked() {
+ 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
+ // not remove any windows from allAppWindows at all if win is exiting and
+ // currently animating away. This ensures that winNdx is monotonically decreasing
+ // and never beyond allAppWindows bounds.
+ i = Math.min(i - 1, allAppWindows.size() - 1)) {
+ WindowState candidate = allAppWindows.get(i);
+ if (candidate.mWillReplaceWindow == false) {
+ continue;
+ }
+ candidate.mWillReplaceWindow = false;
+ service.removeWindowLocked(candidate);
+ }
+ }
+
@Override
void dump(PrintWriter pw, String prefix) {
super.dump(pw, prefix);
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 3db9ae0..2e424d0 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -506,7 +506,7 @@
inFreeformSpace = stack != null && stack.mStackId == FREEFORM_WORKSPACE_STACK_ID;
}
- replacing = replacing || (w.mAppToken != null && w.mAppToken.mWillReplaceWindow);
+ replacing = replacing || w.mWillReplaceWindow;
// If the app is executing an animation because the keyguard is going away,
// keep the wallpaper during the animation so it doesn't flicker out.
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index a6523a4..6a5183f 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -918,10 +918,6 @@
}
void requestRemovalOfReplacedWindows(WindowState win) {
- final AppWindowToken token = win.mAppToken;
- if (token != null && token.mWillReplaceWindow && token.mReplacingWindow == win) {
- token.mHasReplacedWindow = true;
- }
mRemoveReplacedWindows = true;
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 0c606fe..f915fe9 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -267,6 +267,9 @@
/** Amount of time (in milliseconds) to delay before declaring a window freeze timeout. */
static final int WINDOW_FREEZE_TIMEOUT_DURATION = 2000;
+ /** Amount of time (in milliseconds) to delay before declaring a window replacement timeout. */
+ static final int WINDOW_REPLACEMENT_TIMEOUT_DURATION = 2000;
+
/** Amount of time to allow a last ANR message to exist before freeing the memory. */
static final int LAST_ANR_LIFETIME_DURATION_MSECS = 2 * 60 * 60 * 1000; // Two hours
/**
@@ -1349,10 +1352,7 @@
final AppWindowToken appToken = win.mAppToken;
if (appToken != null) {
if (addToToken) {
- appToken.allAppWindows.add(win);
- }
- if (appToken.mWillReplaceWindow) {
- appToken.mReplacingWindow = win;
+ appToken.addWindow(win);
}
}
}
@@ -2083,14 +2083,15 @@
}
private void prepareWindowReplacementTransition(AppWindowToken atoken) {
- if (atoken == null || !atoken.mWillReplaceWindow || !atoken.mAnimateReplacingWindow) {
+ if (atoken == null) {
return;
}
atoken.allDrawn = false;
WindowState replacedWindow = null;
for (int i = atoken.windows.size() - 1; i >= 0 && replacedWindow == null; i--) {
WindowState candidate = atoken.windows.get(i);
- if (candidate.mExiting) {
+ if (candidate.mExiting && candidate.mWillReplaceWindow
+ && candidate.mAnimateReplacingWindow) {
replacedWindow = candidate;
}
}
@@ -2190,7 +2191,7 @@
+ " app-animation="
+ (win.mAppToken != null ? win.mAppToken.mAppAnimator.animation : null)
+ " mWillReplaceWindow="
- + (win.mAppToken != null ? win.mAppToken.mWillReplaceWindow : false)
+ + win.mWillReplaceWindow
+ " inPendingTransaction="
+ (win.mAppToken != null ? win.mAppToken.inPendingTransaction : false)
+ " mDisplayFrozen=" + mDisplayFrozen);
@@ -2202,13 +2203,13 @@
// animation wouldn't be seen.
if (win.mHasSurface && okToDisplay()) {
final AppWindowToken appToken = win.mAppToken;
- if (appToken != null && appToken.mWillReplaceWindow) {
+ if (win.mWillReplaceWindow) {
// This window is going to be replaced. We need to keep it around until the new one
// gets added, then we will get rid of this one.
if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "Preserving " + win + " until the new one is "
+ "added");
win.mExiting = true;
- appToken.mReplacingRemoveRequested = true;
+ win.mReplacingRemoveRequested = true;
Binder.restoreCallingIdentity(origId);
return;
}
@@ -4042,7 +4043,7 @@
// transition animation
// * or this is an opening app and windows are being replaced.
if (wtoken.hidden == visible || (wtoken.hidden && wtoken.mIsExiting) ||
- (visible && wtoken.mWillReplaceWindow)) {
+ (visible && wtoken.waitingForReplacement())) {
boolean changed = false;
if (DEBUG_APP_TRANSITIONS) Slog.v(
TAG_WM, "Changing app " + wtoken + " hidden=" + wtoken.hidden
@@ -7483,6 +7484,8 @@
public static final int TWO_FINGER_SCROLL_START = 45;
public static final int SHOW_NON_RESIZEABLE_DOCK_TOAST = 46;
+ public static final int WINDOW_REPLACEMENT_TIMEOUT = 47;
+
/**
* Used to denote that an integer field in a message will not be used.
*/
@@ -8065,6 +8068,13 @@
toast.show();
}
break;
+ case WINDOW_REPLACEMENT_TIMEOUT: {
+ final AppWindowToken token = (AppWindowToken) msg.obj;
+ synchronized (mWindowMap) {
+ token.clearTimedoutReplaceesLocked();
+ }
+ }
+ break;
}
if (DEBUG_WINDOW_TRACE) {
Slog.v(TAG_WM, "handleMessage: exit");
@@ -8587,7 +8597,7 @@
final int numTokens = tokens.size();
for (int tokenNdx = 0; tokenNdx < numTokens; ++tokenNdx) {
final AppWindowToken wtoken = tokens.get(tokenNdx);
- if (wtoken.mIsExiting && !wtoken.mWillReplaceWindow) {
+ if (wtoken.mIsExiting && !wtoken.waitingForReplacement()) {
continue;
}
i = reAddAppWindowsLocked(displayContent, i, wtoken);
@@ -8696,8 +8706,8 @@
private void forceHigherLayerIfNeeded(WindowState w, WindowStateAnimator winAnimator,
AppWindowToken wtoken) {
boolean force = false;
- if (wtoken.mWillReplaceWindow && wtoken.mReplacingWindow != w
- && wtoken.mAnimateReplacingWindow) {
+
+ if (w.mWillReplaceWindow) {
// We know that we will be animating a relaunching window in the near future,
// which will receive a z-order increase. We want the replaced window to
// immediately receive the same treatment, e.g. to be above the dock divider.
@@ -10213,26 +10223,20 @@
* @param token Application token for which the activity will be relaunched.
*/
public void setReplacingWindow(IBinder token, boolean animate) {
+ AppWindowToken appWindowToken = null;
synchronized (mWindowMap) {
- AppWindowToken appWindowToken = findAppWindowToken(token);
+ appWindowToken = findAppWindowToken(token);
if (appWindowToken == null || !appWindowToken.isVisible()) {
Slog.w(TAG_WM, "Attempted to set replacing window on non-existing app token " + token);
return;
}
- if (DEBUG_ADD_REMOVE) Slog.d(TAG_WM, "Marking app token " + appWindowToken
- + " as replacing window.");
- appWindowToken.mWillReplaceWindow = true;
- appWindowToken.mHasReplacedWindow = false;
- appWindowToken.mAnimateReplacingWindow = animate;
+ appWindowToken.setReplacingWindows(animate);
+ }
- if (animate) {
- // Set-up dummy animation so we can start treating windows associated with this
- // token like they are in transition before the new app window is ready for us to
- // run the real transition animation.
- if (DEBUG_APP_TRANSITIONS) Slog.v(TAG_WM,
- "setReplacingWindow() Setting dummy animation on: " + appWindowToken);
- appWindowToken.mAppAnimator.setDummyAnimation();
- }
+ if (appWindowToken != null) {
+ mH.removeMessages(H.WINDOW_REPLACEMENT_TIMEOUT);
+ mH.sendMessageDelayed(mH.obtainMessage(H.WINDOW_REPLACEMENT_TIMEOUT, appWindowToken),
+ WINDOW_REPLACEMENT_TIMEOUT_DURATION);
}
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 5a589e3..e4a6806 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -75,6 +75,7 @@
import static android.view.WindowManager.LayoutParams.MATCH_PARENT;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
@@ -403,6 +404,18 @@
// used to start an entering animation earlier.
public boolean mSurfaceSaved = false;
+ // This window will be replaced due to relaunch. This allows window manager
+ // to differentiate between simple removal of a window and replacement. In the latter case it
+ // will preserve the old window until the new one is drawn.
+ boolean mWillReplaceWindow = false;
+ // If true, the replaced window was already requested to be removed.
+ boolean mReplacingRemoveRequested = false;
+ // Whether the replacement of the window should trigger app transition animation.
+ boolean mAnimateReplacingWindow = false;
+ // If not null, the window that will be used to replace the old one. This is being set when
+ // the window is added and unset when this window reports its first draw.
+ WindowState mReplacingWindow = null;
+
/**
* Wake lock for drawing.
* Even though it's slightly more expensive to do so, we will use a separate wake lock
@@ -580,8 +593,7 @@
@Override
public void computeFrameLw(Rect pf, Rect df, Rect of, Rect cf, Rect vf, Rect dcf, Rect sf,
Rect osf) {
- if (mAppToken != null && mAppToken.mWillReplaceWindow
- && (mExiting || !mAppToken.mReplacingRemoveRequested)) {
+ if (mWillReplaceWindow && (mExiting || !mReplacingRemoveRequested)) {
// This window is being replaced and either already got information that it's being
// removed or we are still waiting for some information. Because of this we don't
// want to apply any more changes to it, so it remains in this state until new window
@@ -1343,17 +1355,17 @@
}
void maybeRemoveReplacedWindow() {
- AppWindowToken token = mAppToken;
- if (token != null && token.mWillReplaceWindow && token.mReplacingWindow == this
- && token.mHasReplacedWindow) {
- if (DEBUG_ADD_REMOVE) Slog.d(TAG, "Removing replacing window: " + this);
- token.mWillReplaceWindow = false;
- token.mAnimateReplacingWindow = false;
- token.mReplacingRemoveRequested = false;
- token.mReplacingWindow = null;
- token.mHasReplacedWindow = false;
- for (int i = token.allAppWindows.size() - 1; i >= 0; i--) {
- final WindowState win = token.allAppWindows.get(i);
+ if (mAppToken == null) {
+ return;
+ }
+ for (int i = mAppToken.allAppWindows.size() - 1; i >= 0; i--) {
+ final WindowState win = mAppToken.allAppWindows.get(i);
+ if (DEBUG_ADD_REMOVE) Slog.d(TAG, "Removing replaced window: " + win);
+ if (win.mWillReplaceWindow && win.mReplacingWindow == this) {
+ win.mWillReplaceWindow = false;
+ win.mAnimateReplacingWindow = false;
+ win.mReplacingRemoveRequested = false;
+ win.mReplacingWindow = null;
if (win.mExiting) {
mService.removeWindowInnerLocked(win);
}
@@ -2161,7 +2173,7 @@
+ " " + getWindowTag();
}
- private CharSequence getWindowTag() {
+ CharSequence getWindowTag() {
CharSequence tag = mAttrs.getTitle();
if (tag == null || tag.length() <= 0) {
tag = mAttrs.packageName;
@@ -2259,4 +2271,12 @@
boolean isChildWindow() {
return mAttachedWindow != null;
}
+
+ void setReplacing(boolean animate) {
+ if ((mAttrs.privateFlags & PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH) == 0) {
+ mWillReplaceWindow = true;
+ mReplacingWindow = null;
+ mAnimateReplacingWindow = animate;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 539810d..b3eb173 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1190,7 +1190,7 @@
// different stack. If we suddenly crop it to the new stack bounds, it might get cut off.
// We don't want it to happen, so we let it ignore the stack bounds until it gets removed.
// The window that will replace it will abide them.
- if (isAnimating() && (appToken.mWillReplaceWindow || w.inDockedWorkspace()
+ if (isAnimating() && (w.mWillReplaceWindow || w.inDockedWorkspace()
|| w.inFreeformWorkspace())) {
return;
}
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 50bdf25..82a6b56 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -1192,7 +1192,7 @@
// if app window is removed, or window relayout to invisible. We don't want to
// clear it out for windows that get replaced, because the animation depends on
// the flag to remove the replaced window.
- if (win.mAppToken == null || !win.mAppToken.mWillReplaceWindow) {
+ if (!win.mWillReplaceWindow) {
win.mExiting = false;
}
if (win.mWinAnimator.mAnimLayer > layer) {