Clean up closing apps list when clearing anAppWindowToken's task.

Previously it was possible for an AppWindowToken to be removed while
on the closing apps list, used in transition animations. During these
transitions, the visibility of the token is modified. Since
visibility relies on the WindowContainer parent, a
NullPointerException would occur.

This changelist addresses the issue by making sure to remove any
AppWindowToken from this list when its task is set to null.

Change-Id: Id9234822b228f4658f04d42ac0fe7b49ded6f5a1
Fixes: 35352214
Test: manual (primarily code inspection)
This commit is contained in:
Bryce Lee
2017-02-28 15:30:17 -08:00
parent 5a7c7b8e53
commit 6d41026f1b
15 changed files with 86 additions and 38 deletions

View File

@@ -525,7 +525,8 @@ public class AppWindowContainerController
private boolean createSnapshot() {
final TaskSnapshot snapshot = mService.mTaskSnapshotController.getSnapshot(
mContainer.mTask.mTaskId, mContainer.mTask.mUserId, false /* restoreFromDisk */);
mContainer.getTask().mTaskId, mContainer.getTask().mUserId,
false /* restoreFromDisk */);
if (snapshot == null) {
return false;

View File

@@ -85,13 +85,17 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
final boolean mVoiceInteraction;
// TODO: Use getParent instead?
Task mTask;
private Task mTask;
/** @see WindowContainer#fillsParent() */
private boolean mFillsParent;
boolean layoutConfigChanges;
boolean mShowForAllUsers;
int mTargetSdk;
// Flag set while reparenting to prevent actions normally triggered by an individual parent
// change.
private boolean mReparenting;
// The input dispatching timeout for this application token in nanoseconds.
long mInputDispatchingTimeoutNanos;
@@ -426,10 +430,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
void removeIfPossible() {
mIsExiting = false;
removeAllWindowsIfPossible();
if (mTask != null) {
mTask.mStack.mExitingAppTokens.remove(this);
removeImmediately();
}
removeImmediately();
}
@Override
@@ -483,6 +484,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
removed = true;
stopFreezingScreen(true, true);
if (mService.mFocusedApp == this) {
if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "Removing focused app token:" + this);
mService.mFocusedApp = null;
@@ -664,6 +666,37 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
allDrawnExcludingSaved = false;
}
Task getTask() {
return mTask;
}
/**
* Sets the associated task, cleaning up dependencies when unset.
*/
void setTask(Task task) {
// Note: the following code assumes that the previous task's stack is the same as the
// new task's stack.
if (!mReparenting && mTask != null && mTask.mStack != null) {
mTask.mStack.mExitingAppTokens.remove(this);
}
mTask = task;
}
@Override
void onParentSet() {
super.onParentSet();
// When the associated task is {@code null}, the {@link AppWindowToken} can no longer
// access visual elements like the {@link DisplayContent}. We must remove any associations
// such as animations.
if (!mReparenting && mTask == null) {
// It is possible we have been marked as a closing app earlier. We must remove ourselves
// from this list so we do not participate in any future animations.
mService.mClosingApps.remove(this);
}
}
void postWindowRemoveStartingWindowCleanup(WindowState win) {
// TODO: Something smells about the code below...Is there a better way?
if (startingWindow == win) {
@@ -866,13 +899,24 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
throw new IllegalArgumentException(
"window token=" + this + " already child of task=" + mTask);
}
if (mTask.mStack != task.mStack) {
throw new IllegalArgumentException(
"window token=" + this + " current task=" + mTask
+ " belongs to a different stack than " + task);
}
if (DEBUG_ADD_REMOVE) Slog.i(TAG, "reParentWindowToken: removing window token=" + this
+ " from task=" + mTask);
final DisplayContent prevDisplayContent = getDisplayContent();
mReparenting = true;
getParent().removeChild(this);
task.addChild(this, position);
mReparenting = false;
// Relayout display(s).
final DisplayContent displayContent = task.getDisplayContent();
displayContent.setLayoutNeeded();

View File

@@ -588,7 +588,7 @@ public class DockedStackDividerController implements DimLayerUser {
private boolean containsAppInDockedStack(ArraySet<AppWindowToken> apps) {
for (int i = apps.size() - 1; i >= 0; i--) {
final AppWindowToken token = apps.valueAt(i);
if (token.mTask != null && token.mTask.mStack.mStackId == DOCKED_STACK_ID) {
if (token.getTask() != null && token.getTask().mStack.mStackId == DOCKED_STACK_ID) {
return true;
}
}

View File

@@ -133,7 +133,7 @@ class Task extends WindowContainer<AppWindowToken> implements DimLayer.DimLayerU
void addChild(AppWindowToken wtoken, int position) {
position = getAdjustedAddPosition(position);
super.addChild(wtoken, position);
wtoken.mTask = this;
wtoken.setTask(this);
mDeferRemoval = false;
}
@@ -244,7 +244,8 @@ class Task extends WindowContainer<AppWindowToken> implements DimLayer.DimLayerU
removeIfPossible();
}
}
token.mTask = null;
token.setTask(null /*task*/);
}
void setSendingToBottom(boolean toBottom) {

View File

@@ -106,8 +106,8 @@ class TaskSnapshotCache {
if (taskId != null) {
removeRunningEntry(taskId);
}
if (wtoken.mTask != null) {
mRetrievalCache.remove(wtoken.mTask.mTaskId);
if (wtoken.getTask() != null) {
mRetrievalCache.remove(wtoken.getTask().mTaskId);
}
}

View File

@@ -141,11 +141,12 @@ class TaskSnapshotController {
outClosingTasks.clear();
for (int i = closingApps.size() - 1; i >= 0; i--) {
final AppWindowToken atoken = closingApps.valueAt(i);
final Task task = atoken.getTask();
// If the task of the app is not visible anymore, it means no other app in that task
// is opening. Thus, the task is closing.
if (atoken.mTask != null && !atoken.mTask.isVisible()) {
outClosingTasks.add(closingApps.valueAt(i).mTask);
if (task != null && !task.isVisible()) {
outClosingTasks.add(task);
}
}
}

View File

@@ -96,9 +96,11 @@ class TaskSnapshotSurface implements StartingSurface {
// TODO: Inherit behavior whether to draw behind status bar/nav bar.
layoutParams.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
layoutParams.setTitle(String.format(TITLE_FORMAT, token.mTask.mTaskId));
if (token.mTask != null) {
final TaskDescription taskDescription = token.mTask.getTaskDescription();
final Task task = token.getTask();
if (task != null) {
layoutParams.setTitle(String.format(TITLE_FORMAT,task.mTaskId));
final TaskDescription taskDescription = task.getTaskDescription();
if (taskDescription != null) {
fillBackgroundColor = taskDescription.getBackgroundColor();
}

View File

@@ -644,7 +644,7 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
}
for (int appNdx = mExitingAppTokens.size() - 1; appNdx >= 0; --appNdx) {
final AppWindowToken wtoken = mExitingAppTokens.get(appNdx);
if (wtoken.mTask == task) {
if (wtoken.getTask() == task) {
wtoken.mIsExiting = false;
mExitingAppTokens.remove(appNdx);
}

View File

@@ -1445,9 +1445,9 @@ public class WindowManagerService extends IWindowManager.Stub
if (displayContent.isDefaultDisplay) {
final DisplayInfo displayInfo = displayContent.getDisplayInfo();
final Rect taskBounds;
if (atoken != null && atoken.mTask != null) {
if (atoken != null && atoken.getTask() != null) {
taskBounds = mTmpRect;
atoken.mTask.getBounds(mTmpRect);
atoken.getTask().getBounds(mTmpRect);
} else {
taskBounds = null;
}
@@ -2292,7 +2292,7 @@ public class WindowManagerService extends IWindowManager.Stub
// is running.
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "WM#applyAnimationLocked");
if (okToDisplay()) {
final DisplayContent displayContent = atoken.mTask.getDisplayContent();
final DisplayContent displayContent = atoken.getTask().getDisplayContent();
final DisplayInfo displayInfo = displayContent.getDisplayInfo();
final int width = displayInfo.appWidth;
final int height = displayInfo.appHeight;
@@ -2333,7 +2333,7 @@ public class WindowManagerService extends IWindowManager.Stub
final Configuration displayConfig = displayContent.getConfiguration();
Animation a = mAppTransition.loadAnimation(lp, transit, enter, displayConfig.uiMode,
displayConfig.orientation, frame, displayFrame, insets, surfaceInsets,
isVoiceInteraction, freeform, atoken.mTask.mTaskId);
isVoiceInteraction, freeform, atoken.getTask().mTaskId);
if (a != null) {
if (DEBUG_ANIM) logWithStack(TAG, "Loaded animation " + a + " for " + atoken);
final int containingWidth = frame.width();
@@ -2553,8 +2553,8 @@ public class WindowManagerService extends IWindowManager.Stub
}
void setFocusTaskRegionLocked(AppWindowToken previousFocus) {
final Task focusedTask = mFocusedApp != null ? mFocusedApp.mTask : null;
final Task previousTask = previousFocus != null ? previousFocus.mTask : null;
final Task focusedTask = mFocusedApp != null ? mFocusedApp.getTask() : null;
final Task previousTask = previousFocus != null ? previousFocus.getTask() : null;
final DisplayContent focusedDisplayContent =
focusedTask != null ? focusedTask.getDisplayContent() : null;
final DisplayContent previousDisplayContent =
@@ -5085,8 +5085,8 @@ public class WindowManagerService extends IWindowManager.Stub
// Also don't use mInputMethodTarget's stack, because some window with FLAG_NOT_FOCUSABLE
// and FLAG_ALT_FOCUSABLE_IM flags both set might be set to IME target so they're moved
// to make room for IME, but the window is not the focused window that's taking input.
return (mFocusedApp != null && mFocusedApp.mTask != null) ?
mFocusedApp.mTask.mStack : null;
return (mFocusedApp != null && mFocusedApp.getTask() != null) ?
mFocusedApp.getTask().mStack : null;
}
public boolean detectSafeMode() {

View File

@@ -1202,7 +1202,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
Task getTask() {
return mAppToken != null ? mAppToken.mTask : null;
return mAppToken != null ? mAppToken.getTask() : null;
}
TaskStack getStack() {
@@ -2378,8 +2378,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
/** @return true if this window desires touch events. */
boolean canReceiveTouchInput() {
return mAppToken != null && mAppToken.mTask != null
&& mAppToken.mTask.mStack.shouldIgnoreInput();
return mAppToken != null && mAppToken.getTask() != null
&& mAppToken.getTask().mStack.shouldIgnoreInput();
}
@Override

View File

@@ -651,7 +651,7 @@ class WindowSurfacePlacer {
if (openingAppAnimator == null || openingAppAnimator.animation == null) {
return;
}
final int taskId = appToken.mTask.mTaskId;
final int taskId = appToken.getTask().mTaskId;
Bitmap thumbnailHeader = mService.mAppTransition.getAppTransitionThumbnailHeader(taskId);
if (thumbnailHeader == null || thumbnailHeader.getConfig() == Bitmap.Config.ALPHA_8) {
if (DEBUG_APP_TRANSITIONS) Slog.d(TAG, "No thumbnail header bitmap for: " + taskId);

View File

@@ -138,19 +138,18 @@ public class AppWindowContainerControllerTests extends WindowTestsBase {
@Test
public void testReparent() throws Exception {
final StackWindowController stackController =
createStackControllerOnDisplay(sDisplayContent);
final TestTaskWindowContainerController taskController1 =
new TestTaskWindowContainerController(
createStackControllerOnDisplay(sDisplayContent));
new TestTaskWindowContainerController(stackController);
final TestAppWindowContainerController appWindowController1 = createAppWindowController(
taskController1);
final TestTaskWindowContainerController taskController2 =
new TestTaskWindowContainerController(
createStackControllerOnDisplay(sDisplayContent));
new TestTaskWindowContainerController(stackController);
final TestAppWindowContainerController appWindowController2 = createAppWindowController(
taskController2);
final TestTaskWindowContainerController taskController3 =
new TestTaskWindowContainerController(
createStackControllerOnDisplay(sDisplayContent));
new TestTaskWindowContainerController(stackController);
try {
appWindowController1.reparent(taskController1, 0);

View File

@@ -54,7 +54,7 @@ public class DisplayContentTests extends WindowTestsBase {
sDisplayContent, "exiting app");
final AppWindowToken exitingAppToken = exitingAppWindow.mAppToken;
exitingAppToken.mIsExiting = true;
exitingAppToken.mTask.mStack.mExitingAppTokens.add(exitingAppToken);
exitingAppToken.getTask().mStack.mExitingAppTokens.add(exitingAppToken);
assertForAllWindowsOrder(Arrays.asList(
sWallpaperWindow,

View File

@@ -49,7 +49,7 @@ public class TaskSnapshotControllerTest extends WindowTestsBase {
final ArraySet<Task> closingTasks = new ArraySet<>();
sWm.mTaskSnapshotController.getClosingTasks(closingApps, closingTasks);
assertEquals(1, closingTasks.size());
assertEquals(closingWindow.mAppToken.mTask, closingTasks.valueAt(0));
assertEquals(closingWindow.mAppToken.getTask(), closingTasks.valueAt(0));
}
@Test

View File

@@ -167,7 +167,7 @@ class WindowTestsBase {
final TestTaskWindowContainerController taskController =
new TestTaskWindowContainerController(stackController);
TestAppWindowToken appWinToken = new TestAppWindowToken(sDisplayContent);
appWinToken.mTask = taskController.mContainer;
appWinToken.setTask(taskController.mContainer);
final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, name);
win.mAppToken = appWinToken;
return win;