Save window when an app died while it's visible
- If an app died visible, keep the dead window and leave it on screen.
- Apply 50% dim over the dead window to indicate it's no longer active.
- Monitor touch inputs on the dead window and restart the app on tap.
bug: 24913379
Change-Id: I911da4e6135f2bffaf3b1bbe6f911ff689a278ff
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index eb79ae7..ee9aa11 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -1456,29 +1456,35 @@
}
if (r.app == null || r.app.thread == null) {
- // This activity needs to be visible, but isn't even running...
- // get it started and resume if no other stack in this stack is resumed.
- if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
- "Start and freeze screen for " + r);
- if (r != starting) {
- r.startFreezingScreenLocked(r.app, configChanges);
- }
- if (!r.visible || r.mLaunchTaskBehind) {
+ // We need to make sure the app is running if it's the top, or it is
+ // just made visible from invisible.
+ // If the app is already visible, it must have died while it was visible.
+ // In this case, we'll show the dead window but will not restart the app.
+ // Otherwise we could end up thrashing.
+ if (r == top || !r.visible) {
+ // This activity needs to be visible, but isn't even running...
+ // get it started and resume if no other stack in this stack is resumed.
if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
- "Starting and making visible: " + r);
- setVisible(r, true);
- }
- if (r != starting) {
- mStackSupervisor.startSpecificActivityLocked(
- r, noStackActivityResumed, false);
- if (activityNdx >= activities.size()) {
- // Record may be removed if its process needs to restart.
- activityNdx = activities.size() - 1;
- } else {
- noStackActivityResumed = false;
+ "Start and freeze screen for " + r);
+ if (r != starting) {
+ r.startFreezingScreenLocked(r.app, configChanges);
+ }
+ if (!r.visible || r.mLaunchTaskBehind) {
+ if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
+ "Starting and making visible: " + r);
+ setVisible(r, true);
+ }
+ if (r != starting) {
+ mStackSupervisor.startSpecificActivityLocked(
+ r, noStackActivityResumed, false);
+ if (activityNdx >= activities.size()) {
+ // Record may be removed if its process needs to restart.
+ activityNdx = activities.size() - 1;
+ } else {
+ noStackActivityResumed = false;
+ }
}
}
-
} else if (r.visible) {
// If this activity is already visible, then there is nothing
// else to do here.
@@ -3731,10 +3737,13 @@
// Don't currently have state for the activity, or
// it is finishing -- always remove it.
remove = true;
- } else if (r.launchCount > 2 &&
- r.lastLaunchTime > (SystemClock.uptimeMillis()-60000)) {
+ } else if (!r.visible && r.launchCount > 2 &&
+ r.lastLaunchTime > (SystemClock.uptimeMillis() - 60000)) {
// We have launched this activity too many times since it was
// able to run, so give up and remove it.
+ // (Note if the activity is visible, we don't remove the record.
+ // We leave the dead window on the screen but the process will
+ // not be restarted unless user explicitly tap on it.)
remove = true;
} else {
// The process may be gone, but the activity lives on!
@@ -3764,7 +3773,11 @@
if (DEBUG_APP) Slog.v(TAG_APP,
"Clearing app during removeHistory for activity " + r);
r.app = null;
- r.nowVisible = false;
+ // Set nowVisible to previous visible state. If the app was visible while
+ // it died, we leave the dead window on screen so it's basically visible.
+ // This is needed when user later tap on the dead window, we need to stop
+ // other apps when user transfers focus to the restarted activity.
+ r.nowVisible = r.visible;
if (!r.haveState) {
if (DEBUG_SAVED_STATE) Slog.i(TAG_SAVED_STATE,
"App died, clearing saved state of " + r);
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 0afc715..b025ce2 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -2872,25 +2872,25 @@
return;
}
- int stackId = task.stack.mStackId;
if (task.mResizeable && options != null) {
ActivityOptions opts = new ActivityOptions(options);
if (opts.hasBounds()) {
Rect bounds = opts.getBounds();
task.updateOverrideConfiguration(bounds);
+ final int stackId = task.getLaunchStackId();
+ if (stackId != task.stack.mStackId) {
+ moveTaskToStackUncheckedLocked(task, stackId, ON_TOP, !FORCE_FOCUS, reason);
+ // moveTaskToStackUncheckedLocked() should already placed the task on top,
+ // still need moveTaskToFrontLocked() below for any transition settings.
+ }
+ // WM resizeTask must be done after the task is moved to the correct stack,
+ // because Task's setBounds() also updates dim layer's bounds, but that has
+ // dependency on the stack.
mWindowManager.resizeTask(task.taskId, bounds, task.mOverrideConfig,
false /*relayout*/, false /*forced*/);
- stackId = task.getLaunchStackId();
}
}
- if (stackId != task.stack.mStackId) {
- moveTaskToStackUncheckedLocked(task, stackId, ON_TOP, !FORCE_FOCUS, reason);
-
- // moveTaskToStackUncheckedLocked() should already placed the task on top,
- // still need moveTaskToFrontLocked() below for any transition settings.
- }
-
final ActivityRecord r = task.getTopActivity();
task.stack.moveTaskToFrontLocked(task, false /* noAnimation */, options,
r == null ? null : r.appTimeTracker, reason);
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index c5bd3a7..9143097 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -102,6 +102,7 @@
// Set to true when the token has been removed from the window mgr.
boolean removed;
+ boolean appDied;
// Information about an application starting window if displayed.
StartingData startingData;
WindowState startingWindow;
@@ -365,6 +366,26 @@
windows.clear();
}
+ void removeAllDeadWindows() {
+ for (int winNdx = allAppWindows.size() - 1; winNdx >= 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.
+ winNdx = Math.min(winNdx - 1, allAppWindows.size() - 1)) {
+ WindowState win = allAppWindows.get(winNdx);
+ if (win.mAppDied) {
+ if (WindowManagerService.DEBUG_WINDOW_MOVEMENT) {
+ Slog.w(WindowManagerService.TAG, "removeAllDeadWindows: " + win);
+ }
+ // Set mDestroying, we don't want any animation or delayed removal here.
+ win.mDestroying = true;
+ service.removeWindowLocked(win);
+ }
+ }
+ }
+
@Override
void dump(PrintWriter pw, String prefix) {
super.dump(pw, prefix);
diff --git a/services/core/java/com/android/server/wm/DimBehindController.java b/services/core/java/com/android/server/wm/DimLayerController.java
similarity index 78%
rename from services/core/java/com/android/server/wm/DimBehindController.java
rename to services/core/java/com/android/server/wm/DimLayerController.java
index 8870dd1..f9aca00 100644
--- a/services/core/java/com/android/server/wm/DimBehindController.java
+++ b/services/core/java/com/android/server/wm/DimLayerController.java
@@ -1,6 +1,7 @@
package com.android.server.wm;
import static com.android.server.wm.WindowManagerService.DEBUG_DIM_LAYER;
+import static com.android.server.wm.WindowManagerService.LAYER_OFFSET_DIM;
import android.graphics.Rect;
import android.util.ArrayMap;
@@ -11,32 +12,38 @@
/**
* Centralizes the control of dim layers used for
- * {@link android.view.WindowManager.LayoutParams#FLAG_DIM_BEHIND}.
+ * {@link android.view.WindowManager.LayoutParams#FLAG_DIM_BEHIND}
+ * as well as other use cases (such as dimming above a dead window).
*/
-class DimBehindController {
- private static final String TAG = "DimBehindController";
+class DimLayerController {
+ private static final String TAG = "DimLayerController";
/** Amount of time in milliseconds to animate the dim surface from one value to another,
* when no window animation is driving it. */
private static final int DEFAULT_DIM_DURATION = 200;
- // Shared dim layer for fullscreen users. {@link DimBehindState#dimLayer} will point to this
+ /**
+ * The default amount of dim applied over a dead window
+ */
+ private static final float DEFAULT_DIM_AMOUNT_DEAD_WINDOW = 0.5f;
+
+ // Shared dim layer for fullscreen users. {@link DimLayerState#dimLayer} will point to this
// instead of creating a new object per fullscreen task on a display.
private DimLayer mSharedFullScreenDimLayer;
- private ArrayMap<DimLayer.DimLayerUser, DimBehindState> mState = new ArrayMap<>();
+ private ArrayMap<DimLayer.DimLayerUser, DimLayerState> mState = new ArrayMap<>();
private DisplayContent mDisplayContent;
private Rect mTmpBounds = new Rect();
- DimBehindController(DisplayContent displayContent) {
+ DimLayerController(DisplayContent displayContent) {
mDisplayContent = displayContent;
}
/** Updates the dim layer bounds, recreating it if needed. */
void updateDimLayer(DimLayer.DimLayerUser dimLayerUser) {
- DimBehindState state = getOrCreateDimBehindState(dimLayerUser);
+ DimLayerState state = getOrCreateDimLayerState(dimLayerUser, false);
final boolean previousFullscreen = state.dimLayer != null
&& state.dimLayer == mSharedFullScreenDimLayer;
DimLayer newDimLayer;
@@ -60,7 +67,7 @@
newDimLayer.setBounds(mTmpBounds);
mSharedFullScreenDimLayer = newDimLayer;
} else if (state.dimLayer != null) {
- state.dimLayer. destroySurface();
+ state.dimLayer.destroySurface();
}
} else {
newDimLayer = (state.dimLayer == null || previousFullscreen)
@@ -72,19 +79,21 @@
state.dimLayer = newDimLayer;
}
- private DimBehindState getOrCreateDimBehindState(DimLayer.DimLayerUser dimLayerUser) {
- if (DEBUG_DIM_LAYER) Slog.v(TAG, "getDimBehindState, dimLayerUser="
+ private DimLayerState getOrCreateDimLayerState(
+ DimLayer.DimLayerUser dimLayerUser, boolean aboveApp) {
+ if (DEBUG_DIM_LAYER) Slog.v(TAG, "getOrCreateDimLayerState, dimLayerUser="
+ dimLayerUser.toShortString());
- DimBehindState state = mState.get(dimLayerUser);
+ DimLayerState state = mState.get(dimLayerUser);
if (state == null) {
- state = new DimBehindState();
+ state = new DimLayerState();
mState.put(dimLayerUser, state);
}
+ state.dimAbove = aboveApp;
return state;
}
private void setContinueDimming(DimLayer.DimLayerUser dimLayerUser) {
- DimBehindState state = mState.get(dimLayerUser);
+ DimLayerState state = mState.get(dimLayerUser);
if (state == null) {
if (DEBUG_DIM_LAYER) Slog.w(TAG, "setContinueDimming, no state for: "
+ dimLayerUser.toShortString());
@@ -95,7 +104,7 @@
boolean isDimming() {
for (int i = mState.size() - 1; i >= 0; i--) {
- DimBehindState state = mState.valueAt(i);
+ DimLayerState state = mState.valueAt(i);
if (state.dimLayer != null && state.dimLayer.isDimming()) {
return true;
}
@@ -110,15 +119,15 @@
}
private boolean getContinueDimming(DimLayer.DimLayerUser dimLayerUser) {
- DimBehindState state = mState.get(dimLayerUser);
+ DimLayerState state = mState.get(dimLayerUser);
return state != null && state.continueDimming;
}
void startDimmingIfNeeded(DimLayer.DimLayerUser dimLayerUser,
- WindowStateAnimator newWinAnimator) {
+ WindowStateAnimator newWinAnimator, boolean aboveApp) {
// Only set dim params on the highest dimmed layer.
// Don't turn on for an unshown surface, or for any layer but the highest dimmed layer.
- DimBehindState state = getOrCreateDimBehindState(dimLayerUser);
+ DimLayerState state = getOrCreateDimLayerState(dimLayerUser, aboveApp);
if (DEBUG_DIM_LAYER) Slog.v(TAG, "startDimmingIfNeeded,"
+ " dimLayerUser=" + dimLayerUser.toShortString()
+ " newWinAnimator=" + newWinAnimator
@@ -145,7 +154,7 @@
private void stopDimmingIfNeeded(DimLayer.DimLayerUser dimLayerUser) {
// No need to check if state is null, we know the key has a value.
- DimBehindState state = mState.get(dimLayerUser);
+ DimLayerState state = mState.get(dimLayerUser);
if (DEBUG_DIM_LAYER) Slog.v(TAG, "stopDimmingIfNeeded,"
+ " dimLayerUser=" + dimLayerUser.toShortString()
+ " state.continueDimming=" + state.continueDimming
@@ -188,7 +197,7 @@
}
private boolean animateDimLayers(DimLayer.DimLayerUser dimLayerUser) {
- DimBehindState state = mState.get(dimLayerUser);
+ DimLayerState state = mState.get(dimLayerUser);
if (DEBUG_DIM_LAYER) Slog.v(TAG, "animateDimLayers,"
+ " dimLayerUser=" + dimLayerUser.toShortString()
+ " state.animator=" + state.animator
@@ -199,8 +208,13 @@
dimLayer = state.dimLayer.getLayer();
dimAmount = 0;
} else {
- dimLayer = state.animator.mAnimLayer - WindowManagerService.LAYER_OFFSET_DIM;
- dimAmount = state.animator.mWin.mAttrs.dimAmount;
+ if (state.dimAbove) {
+ dimLayer = state.animator.mAnimLayer + LAYER_OFFSET_DIM;
+ dimAmount = DEFAULT_DIM_AMOUNT_DEAD_WINDOW;
+ } else {
+ dimLayer = state.animator.mAnimLayer - LAYER_OFFSET_DIM;
+ dimAmount = state.animator.mWin.mAttrs.dimAmount;
+ }
}
final float targetAlpha = state.dimLayer.getTargetAlpha();
if (targetAlpha != dimAmount) {
@@ -211,7 +225,7 @@
? state.animator.mAnimation.computeDurationHint()
: DEFAULT_DIM_DURATION;
if (targetAlpha > dimAmount) {
- duration = getDimBehindFadeDuration(duration);
+ duration = getDimLayerFadeDuration(duration);
}
state.dimLayer.show(dimLayer, dimAmount, duration);
}
@@ -230,11 +244,11 @@
}
boolean isDimming(DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator winAnimator) {
- DimBehindState state = mState.get(dimLayerUser);
+ DimLayerState state = mState.get(dimLayerUser);
return state != null && state.animator == winAnimator && state.dimLayer.isDimming();
}
- private long getDimBehindFadeDuration(long duration) {
+ private long getDimLayerFadeDuration(long duration) {
TypedValue tv = new TypedValue();
mDisplayContent.mService.mContext.getResources().getValue(
com.android.internal.R.fraction.config_dimBehindFadeDuration, tv, true);
@@ -248,7 +262,7 @@
void close() {
for (int i = mState.size() - 1; i >= 0; i--) {
- DimBehindState state = mState.valueAt(i);
+ DimLayerState state = mState.valueAt(i);
state.dimLayer.destroySurface();
}
mState.clear();
@@ -256,10 +270,23 @@
}
void removeDimLayerUser(DimLayer.DimLayerUser dimLayerUser) {
- mState.remove(dimLayerUser);
+ DimLayerState state = mState.get(dimLayerUser);
+ if (state != null) {
+ state.dimLayer.destroySurface();
+ mState.remove(dimLayerUser);
+ }
}
void applyDimBehind(DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator animator) {
+ applyDim(dimLayerUser, animator, false /* aboveApp */);
+ }
+
+ void applyDimAbove(DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator animator) {
+ applyDim(dimLayerUser, animator, true /* aboveApp */);
+ }
+
+ private void applyDim(
+ DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator animator, boolean aboveApp) {
if (dimLayerUser == null) {
Slog.e(TAG, "Trying to apply dim layer for: " + this
+ ", but no dim layer user found.");
@@ -269,26 +296,27 @@
setContinueDimming(dimLayerUser);
if (!isDimming(dimLayerUser, animator)) {
if (DEBUG_DIM_LAYER) Slog.v(TAG, "Win " + this + " start dimming.");
- startDimmingIfNeeded(dimLayerUser, animator);
+ startDimmingIfNeeded(dimLayerUser, animator, aboveApp);
}
}
}
- private static class DimBehindState {
- // The particular window with FLAG_DIM_BEHIND set. If null, hide dimLayer.
+ private static class DimLayerState {
+ // The particular window requesting a dim layer. If null, hide dimLayer.
WindowStateAnimator animator;
// Set to false at the start of performLayoutAndPlaceSurfaces. If it is still false by the
// end then stop any dimming.
boolean continueDimming;
DimLayer dimLayer;
+ boolean dimAbove;
}
void dump(String prefix, PrintWriter pw) {
- pw.println(prefix + "DimBehindController");
+ pw.println(prefix + "DimLayerController");
for (int i = 0, n = mState.size(); i < n; i++) {
pw.println(prefix + " " + mState.keyAt(i).toShortString());
pw.print(prefix + " ");
- DimBehindState state = mState.valueAt(i);
+ DimLayerState state = mState.valueAt(i);
pw.print("dimLayer=" + (state.dimLayer == mSharedFullScreenDimLayer ? "shared" :
state.dimLayer));
pw.print(", animator=" + state.animator);
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index d6f807e..53f8bbd 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -116,7 +116,7 @@
final DockedStackDividerController mDividerControllerLocked;
- final DimBehindController mDimBehindController;
+ final DimLayerController mDimLayerController;
/**
* @param display May not be null.
@@ -131,7 +131,7 @@
mService = service;
initializeDisplayBaseInfo();
mDividerControllerLocked = new DockedStackDividerController(service.mContext, this);
- mDimBehindController = new DimBehindController(this);
+ mDimLayerController = new DimLayerController(this);
}
int getDisplayId() {
@@ -271,7 +271,7 @@
}
void detachStack(TaskStack stack) {
- mDimBehindController.removeDimLayerUser(stack);
+ mDimLayerController.removeDimLayerUser(stack);
mStacks.remove(stack);
}
@@ -415,23 +415,23 @@
}
boolean animateDimLayers() {
- return mDimBehindController.animateDimLayers();
+ return mDimLayerController.animateDimLayers();
}
void resetDimming() {
- mDimBehindController.resetDimming();
+ mDimLayerController.resetDimming();
}
boolean isDimming() {
- return mDimBehindController.isDimming();
+ return mDimLayerController.isDimming();
}
void stopDimmingIfNeeded() {
- mDimBehindController.stopDimmingIfNeeded();
+ mDimLayerController.stopDimmingIfNeeded();
}
void close() {
- mDimBehindController.close();
+ mDimLayerController.close();
for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
mStacks.get(stackNdx).close();
}
@@ -578,7 +578,7 @@
}
}
pw.println();
- mDimBehindController.dump(prefix + " ", pw);
+ mDimLayerController.dump(prefix + " ", pw);
}
@Override
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index dbf13fe..5864b25 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -116,7 +116,7 @@
mDeferRemoval = false;
DisplayContent content = getDisplayContent();
if (content != null) {
- content.mDimBehindController.removeDimLayerUser(this);
+ content.mDimLayerController.removeDimLayerUser(this);
}
mStack.removeTask(this);
mService.mTaskIdToTask.delete(mTaskId);
@@ -220,7 +220,7 @@
mBounds.set(bounds);
mRotation = rotation;
if (displayContent != null) {
- displayContent.mDimBehindController.updateDimLayer(this);
+ displayContent.mDimLayerController.updateDimLayer(this);
}
mOverrideConfig = mFullscreen ? Configuration.EMPTY : config;
return boundsChange;
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 95e2391..f72384c 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -176,7 +176,7 @@
}
if (mDisplayContent != null) {
- mDisplayContent.mDimBehindController.updateDimLayer(this);
+ mDisplayContent.mDimLayerController.updateDimLayer(this);
mAnimationBackgroundSurface.setBounds(bounds);
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 230e81b..d5304c2 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1924,6 +1924,11 @@
return WindowManagerGlobal.ADD_INVALID_DISPLAY;
}
+ if (atoken != null && atoken.appDied) {
+ Slog.d(TAG, "App is now revived: " + atoken);
+ atoken.appDied = false;
+ }
+
mPolicy.adjustWindowParamsLw(win.mAttrs);
win.setShowToOwnerOnlyLocked(mPolicy.checkShowToOwnerOnly(attrs));
@@ -1935,11 +1940,7 @@
final boolean openInputChannels = (outInputChannel != null
&& (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
if (openInputChannels) {
- String name = win.makeInputChannelName();
- InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
- win.setInputChannel(inputChannels[0]);
- inputChannels[1].transferTo(outInputChannel);
- mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle);
+ win.openInputChannel(outInputChannel);
}
// From now on, no exceptions or errors allowed!
@@ -2183,13 +2184,31 @@
+ "added");
win.mExiting = true;
appToken.mReplacingRemoveRequested = true;
+ Binder.restoreCallingIdentity(origId);
return;
}
// If we are not currently running the exit animation, we
// need to see about starting one.
wasVisible = win.isWinVisibleLw();
- if (wasVisible) {
+ if (wasVisible && appToken != null && appToken.appDied) {
+ if (DEBUG_ADD_REMOVE) Slog.v(TAG,
+ "Not removing " + win + " because app died while it's visible");
+
+ win.mAppDied = true;
+ win.setDisplayLayoutNeeded();
+ mWindowPlacerLocked.performSurfacePlacement();
+
+ // Set up a replacement input channel since the app is now dead.
+ // We need to catch tapping on the dead window to restart the app.
+ win.openInputChannel(null);
+ mInputMonitor.updateInputWindowsLw(true /*force*/);
+
+ Binder.restoreCallingIdentity(origId);
+ return;
+ }
+
+ if (wasVisible) {
final int transit = (!startingWindow)
? WindowManagerPolicy.TRANSIT_EXIT
: WindowManagerPolicy.TRANSIT_PREVIEW_DONE;
@@ -2240,10 +2259,6 @@
}
void removeWindowInnerLocked(WindowState win) {
- removeWindowInnerLocked(win, true);
- }
-
- private void removeWindowInnerLocked(WindowState win, boolean performLayout) {
if (win.mRemoved) {
// Nothing to do.
return;
@@ -2338,9 +2353,7 @@
if (!mWindowPlacerLocked.isInLayout()) {
assignLayersLocked(windows);
win.setDisplayLayoutNeeded();
- if (performLayout) {
- mWindowPlacerLocked.performSurfacePlacement();
- }
+ mWindowPlacerLocked.performSurfacePlacement();
if (win.mAppToken != null) {
win.mAppToken.updateReportedVisibilityLocked();
}
@@ -4118,6 +4131,14 @@
wtoken.waitingToShow = false;
wtoken.hiddenRequested = !visible;
+ if (!visible && wtoken.appDied) {
+ // This app is dead while it was visible, we kept its dead window on screen.
+ // Now that the app is going invisible, we can remove it. It will be restarted
+ // if made visible again.
+ wtoken.appDied = false;
+ wtoken.removeAllWindows();
+ }
+
// If we are preparing an app transition, then delay changing
// the visibility of this token until we execute that transition.
if (okToDisplay() && mAppTransition.isTransitionSet()) {
@@ -8548,7 +8569,7 @@
final DimLayer.DimLayerUser dimLayerUser = w.getDimLayerUser();
final DisplayContent displayContent = w.getDisplayContent();
if (layerChanged && dimLayerUser != null && displayContent != null &&
- displayContent.mDimBehindController.isDimming(dimLayerUser, winAnimator)) {
+ displayContent.mDimLayerController.isDimming(dimLayerUser, winAnimator)) {
// Force an animation pass just to update the mDimLayer layer.
scheduleAnimationLocked();
}
@@ -8667,6 +8688,13 @@
+ " dragResizingChanged=" + dragResizingChanged);
}
+ // If it's a dead window left on screen, and the configuration changed,
+ // there is nothing we can do about it. Remove the window now.
+ if (w.mAppToken != null && w.mAppDied) {
+ w.mAppToken.removeAllDeadWindows();
+ return;
+ }
+
w.mLastOverscanInsets.set(w.mOverscanInsets);
w.mLastContentInsets.set(w.mContentInsets);
w.mLastVisibleInsets.set(w.mVisibleInsets);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index c1fa78a..984a353 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -340,6 +340,12 @@
boolean mRemoveOnExit;
/**
+ * Whether the app died while it was visible, if true we might need
+ * to continue to show it until it's restarted.
+ */
+ boolean mAppDied;
+
+ /**
* Set when the orientation is changing and this window has not yet
* been updated for the new orientation.
*/
@@ -362,6 +368,7 @@
// Input channel and input window handle used by the input dispatcher.
final InputWindowHandle mInputWindowHandle;
InputChannel mInputChannel;
+ InputChannel mClientChannel;
// Used to improve performance of toString()
String mStringNameCache;
@@ -1274,16 +1281,28 @@
mConfigHasChanged = false;
}
- void setInputChannel(InputChannel inputChannel) {
+ void openInputChannel(InputChannel outInputChannel) {
if (mInputChannel != null) {
throw new IllegalStateException("Window already has an input channel.");
}
-
- mInputChannel = inputChannel;
- mInputWindowHandle.inputChannel = inputChannel;
+ String name = makeInputChannelName();
+ InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
+ mInputChannel = inputChannels[0];
+ mClientChannel = inputChannels[1];
+ mInputWindowHandle.inputChannel = inputChannels[0];
+ if (outInputChannel != null) {
+ mClientChannel.transferTo(outInputChannel);
+ mClientChannel.dispose();
+ mClientChannel = null;
+ }
+ mService.mInputManager.registerInputChannel(mInputChannel, mInputWindowHandle);
}
void disposeInputChannel() {
+ if (mClientChannel != null) {
+ mClientChannel.dispose();
+ mClientChannel = null;
+ }
if (mInputChannel != null) {
mService.mInputManager.unregisterInputChannel(mInputChannel);
@@ -1294,10 +1313,13 @@
mInputWindowHandle.inputChannel = null;
}
- void handleFlagDimBehind() {
- if ((mAttrs.flags & FLAG_DIM_BEHIND) != 0 && mDisplayContent != null && !mExiting
- && isDisplayedLw()) {
- mDisplayContent.mDimBehindController.applyDimBehind(getDimLayerUser(), mWinAnimator);
+ void applyDimLayerIfNeeded() {
+ if (!mExiting && mAppDied) {
+ // If app died visible, apply a dim over the window to indicate that it's inactive
+ mDisplayContent.mDimLayerController.applyDimAbove(getDimLayerUser(), mWinAnimator);
+ } else if ((mAttrs.flags & FLAG_DIM_BEHIND) != 0
+ && mDisplayContent != null && !mExiting && isDisplayedLw()) {
+ mDisplayContent.mDimLayerController.applyDimBehind(getDimLayerUser(), mWinAnimator);
}
}
@@ -1375,6 +1397,9 @@
WindowState win = mService.windowForClientLocked(mSession, mClient, false);
Slog.i(TAG, "WIN DEATH: " + win);
if (win != null) {
+ if (win.mAppToken != null && !win.mAppToken.clientHidden) {
+ win.mAppToken.appDied = true;
+ }
mService.removeWindowLocked(win);
} else if (mHasSurface) {
Slog.e(TAG, "!!! LEAK !!! Window removed but surface still valid.");
@@ -1574,7 +1599,7 @@
public boolean isDimming() {
final DimLayer.DimLayerUser dimLayerUser = getDimLayerUser();
return dimLayerUser != null && mDisplayContent != null &&
- mDisplayContent.mDimBehindController.isDimming(dimLayerUser, mWinAnimator);
+ mDisplayContent.mDimLayerController.isDimming(dimLayerUser, mWinAnimator);
}
public void setShowToOwnerOnlyLocked(boolean showToOwnerOnly) {
@@ -1833,7 +1858,8 @@
pw.print(prefix); pw.print("mToken="); pw.println(mToken);
pw.print(prefix); pw.print("mRootToken="); pw.println(mRootToken);
if (mAppToken != null) {
- pw.print(prefix); pw.print("mAppToken="); pw.println(mAppToken);
+ pw.print(prefix); pw.print("mAppToken="); pw.print(mAppToken);
+ pw.print(" mAppDied=");pw.println(mAppDied);
}
if (mTargetAppToken != null) {
pw.print(prefix); pw.print("mTargetAppToken="); pw.println(mTargetAppToken);
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 8dddbd1..e9be2dd 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1426,12 +1426,13 @@
private void adjustCropToStackBounds(WindowState w, Rect clipRect) {
final AppWindowToken appToken = w.mAppToken;
+ final Task task = w.getTask();
// We don't apply the the stack bounds to the window that is being replaced, because it was
// living in a 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 (appToken != null && appToken.mCropWindowsToStack && !appToken.mWillReplaceWindow) {
- TaskStack stack = w.getTask().mStack;
+ if (task != null && appToken.mCropWindowsToStack && !appToken.mWillReplaceWindow) {
+ TaskStack stack = task.mStack;
stack.getBounds(mTmpStackBounds);
// When we resize we use the big surface approach, which means we can't trust the
// window frame bounds anymore. Instead, the window will be placed at 0, 0, but to avoid
@@ -1543,7 +1544,7 @@
mDsDy * w.mHScale, mDtDy * w.mVScale);
mAnimator.setPendingLayoutChanges(w.getDisplayId(),
WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER);
- w.handleFlagDimBehind();
+ w.applyDimLayerIfNeeded();
} catch (RuntimeException e) {
// If something goes wrong with the surface (such
// as running out of memory), don't take down the
@@ -1859,6 +1860,9 @@
if (mWin.mAttrs.type != TYPE_APPLICATION_STARTING && mWin.mAppToken != null) {
mWin.mAppToken.firstWindowDrawn = true;
+ // We now have a good window to show, remove dead placeholders
+ mWin.mAppToken.removeAllDeadWindows();
+
if (mWin.mAppToken.startingData != null) {
if (WindowManagerService.DEBUG_STARTING_WINDOW ||
WindowManagerService.DEBUG_ANIM) Slog.v(TAG,
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index aca0f5b..eef8e17 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -670,7 +670,7 @@
handleNotObscuredLocked(w, innerDw, innerDh);
}
- w.handleFlagDimBehind();
+ w.applyDimLayerIfNeeded();
if (isDefaultDisplay && obscuredChanged
&& mWallpaperControllerLocked.isWallpaperTarget(w) && w.isVisibleLw()) {
@@ -781,7 +781,7 @@
+ " a=" + winAnimator.mAnimating);
}
}
- if (w != atoken.startingWindow) {
+ if (w != atoken.startingWindow && !w.mAppDied) {
if (!atoken.mAppAnimator.freezingScreen || !w.mAppFreezing) {
atoken.numInterestingWindows++;
if (w.isDrawnLw()) {