Fix back stack for PiP from multi-activity Task

Details in design doc go/pip-and-back

Video: http://recall/-/aaaaaabFQoRHlzixHdtY/g8lbXnC8ln0t54ARxfPELo
Bug: 184758170
Test: ensure back stack works for Netflix, see video
Test: atest PinnedStackTests
Test: atest RootWindowContainerTests
Change-Id: I9e2f8d0d50dd107ff30fd7afe6273d8347aac803
This commit is contained in:
Hongwei Wang
2021-04-07 11:53:28 -07:00
parent e5bb44a832
commit 7f64b14478
7 changed files with 87 additions and 9 deletions

View File

@@ -161,6 +161,7 @@ package android.app {
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void moveTaskToRootTask(int, int, boolean);
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void removeRootTasksInWindowingModes(@NonNull int[]);
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void removeRootTasksWithActivityTypes(@NonNull int[]);
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public boolean removeTask(int);
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void resizeTask(int, android.graphics.Rect);
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void startSystemLockTaskMode(int);
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void stopSystemLockTaskMode();

View File

@@ -469,7 +469,8 @@ public class ActivityTaskManager {
}
}
/** @hide */
/** Removes task by a given taskId */
@RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
public boolean removeTask(int taskId) {
try {
return getService().removeTask(taskId);

View File

@@ -308,6 +308,7 @@ message TaskProto {
optional float minimize_amount = 27;
optional bool created_by_organizer = 28;
optional string affinity = 29;
optional bool has_child_pip_activity = 30;
}
/* represents ActivityRecordProto */

View File

@@ -587,7 +587,14 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
AnimatingActivityRegistry mAnimatingActivityRegistry;
private Task mLastParent;
// Set whenever the ActivityRecord gets reparented to a Task so we can know the last known
// parent was when the ActivityRecord is detached from the hierarchy
private Task mLastKnownParent;
// Set to the previous Task parent of the ActivityRecord when it is reparented to a new Task
// due to picture-in-picture. This gets cleared whenever this activity or the Task
// it references to gets removed. This should also be cleared when we move out of pip.
private Task mLastParentBeforePip;
boolean firstWindowDrawn;
/** Whether the visible window(s) of this activity is drawn. */
@@ -1096,6 +1103,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
pw.println(prefix + "configChanges=0x" + Integer.toHexString(info.configChanges));
}
}
if (mLastParentBeforePip != null) {
pw.println(prefix + "lastParentTaskIdBeforePip=" + mLastParentBeforePip.mTaskId);
}
dumpLetterboxInfo(pw, prefix);
}
@@ -1349,7 +1359,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (getDisplayContent() != null) {
getDisplayContent().mClosingApps.remove(this);
}
} else if (mLastParent != null && mLastParent.getRootTask() != null) {
} else if (mLastKnownParent != null && mLastKnownParent.getRootTask() != null) {
task.getRootTask().mExitingActivities.remove(this);
}
final Task rootTask = getRootTask();
@@ -1362,7 +1372,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
? rootTask.getAnimatingActivityRegistry()
: null;
mLastParent = task;
mLastKnownParent = task;
if (mLastKnownParent == mLastParentBeforePip) {
// Activity's reparented back from pip, clear the links once established
clearLastParentBeforePip();
}
updateColorTransform();
@@ -1381,6 +1395,26 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
}
/**
* Sets {@link #mLastParentBeforePip} to the current parent Task, it's caller's job to ensure
* {@link #getTask()} is set before this is called.
*/
void setLastParentBeforePip() {
mLastParentBeforePip = getTask();
mLastParentBeforePip.mChildPipActivity = this;
}
private void clearLastParentBeforePip() {
if (mLastParentBeforePip != null) {
mLastParentBeforePip.mChildPipActivity = null;
mLastParentBeforePip = null;
}
}
@Nullable Task getLastParentBeforePip() {
return mLastParentBeforePip;
}
private void updateColorTransform() {
if (mSurfaceControl != null && mLastAppSaturationInfo != null) {
getPendingTransaction().setColorTransform(mSurfaceControl,
@@ -3434,6 +3468,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
*/
void cleanUp(boolean cleanServices, boolean setState) {
task.cleanUpActivityReferences(this);
clearLastParentBeforePip();
deferRelaunchUntilPaused = false;
frozenBeforeDestroy = false;

View File

@@ -1415,6 +1415,9 @@ class RecentTasks {
return true;
}
// The given task if always treated as in visible range if it is the origin of pinned task.
if (task.mChildPipActivity != null) return true;
if (mMaxNumVisibleTasks >= 0) {
// Always keep up to the max number of recent tasks, but return false afterwards
return numVisibleTasks <= mMaxNumVisibleTasks;

View File

@@ -2124,6 +2124,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
.setDeferTaskAppear(true)
.setHasBeenVisible(true)
.build();
// Establish bi-directional link between the original and pinned task.
r.setLastParentBeforePip();
// It's possible the task entering PIP is in freeform, so save the last
// non-fullscreen bounds. Then when this new PIP task exits PIP, it can restore
// to its previous freeform bounds.

View File

@@ -131,6 +131,7 @@ import static com.android.server.wm.TaskProto.BOUNDS;
import static com.android.server.wm.TaskProto.CREATED_BY_ORGANIZER;
import static com.android.server.wm.TaskProto.DISPLAY_ID;
import static com.android.server.wm.TaskProto.FILLS_PARENT;
import static com.android.server.wm.TaskProto.HAS_CHILD_PIP_ACTIVITY;
import static com.android.server.wm.TaskProto.LAST_NON_FULLSCREEN_BOUNDS;
import static com.android.server.wm.TaskProto.MIN_HEIGHT;
import static com.android.server.wm.TaskProto.MIN_WIDTH;
@@ -836,6 +837,14 @@ class Task extends WindowContainer<WindowContainer> {
// The task will be removed when TaskOrganizer, which is managing the task, is destroyed.
boolean mRemoveWithTaskOrganizer;
/**
* Reference to the pinned activity that is logically parented to this task, ie.
* the previous top activity within this task is put into pinned mode.
* This always gets cleared in pair with the ActivityRecord-to-Task link as seen in
* {@link ActivityRecord#clearLastParentBeforePip()}.
*/
ActivityRecord mChildPipActivity;
private Task(ActivityTaskManagerService atmService, int _taskId, Intent _intent,
Intent _affinityIntent, String _affinity, String _rootAffinity,
ComponentName _realActivity, ComponentName _origActivity, boolean _rootWasReset,
@@ -1841,6 +1850,10 @@ class Task extends WindowContainer<WindowContainer> {
/** Completely remove all activities associated with an existing task. */
void performClearTask(String reason) {
// The original task is to be removed, try remove also the pinned task.
if (mChildPipActivity != null && mChildPipActivity.getTask() != null) {
mTaskSupervisor.removeRootTask(mChildPipActivity.getTask());
}
// Broken down into to cases to avoid object create due to capturing mStack.
if (getRootTask() == null) {
forAllActivities((r) -> {
@@ -4449,6 +4462,7 @@ class Task extends WindowContainer<WindowContainer> {
}
pw.print(prefix); pw.print("taskId=" + mTaskId);
pw.println(" rootTaskId=" + getRootTaskId());
pw.print(prefix); pw.println("hasChildPipActivity=" + (mChildPipActivity != null));
pw.print(prefix); pw.print("mHasBeenVisible="); pw.println(getHasBeenVisible());
pw.print(prefix); pw.print("mResizeMode=");
pw.print(ActivityInfo.resizeModeToString(mResizeMode));
@@ -5328,7 +5342,6 @@ class Task extends WindowContainer<WindowContainer> {
return;
}
final int currentMode = getWindowingMode();
final int currentOverrideMode = getRequestedOverrideWindowingMode();
final Task topTask = getTopMostTask();
int windowingMode = preferredWindowingMode;
@@ -5397,9 +5410,26 @@ class Task extends WindowContainer<WindowContainer> {
mTaskSupervisor.mNoAnimActivities.add(topActivity);
}
super.setWindowingMode(windowingMode);
// setWindowingMode triggers an onConfigurationChanged cascade which can result in a
// different resolved windowing mode (usually when preferredWindowingMode is UNDEFINED).
windowingMode = getWindowingMode();
// Try reparent pinned activity back to its original task after onConfigurationChanged
// cascade finishes. This is done on Task level instead of
// {@link ActivityRecord#onConfigurationChanged(Configuration)} since when we exit PiP,
// we set final windowing mode on the ActivityRecord first and then on its Task when
// the exit PiP transition finishes. Meanwhile, the exit transition is always
// performed on its original task, reparent immediately in ActivityRecord breaks it.
if (currentMode == WINDOWING_MODE_PINNED) {
if (topActivity != null && topActivity.getLastParentBeforePip() != null) {
// Do not reparent if the pinned task is in removal, indicated by the
// force hidden flag.
if (!isForceHidden()) {
final Task lastParentBeforePip = topActivity.getLastParentBeforePip();
topActivity.reparent(lastParentBeforePip,
lastParentBeforePip.getChildCount() /* top */,
"movePinnedActivityToOriginalTask");
lastParentBeforePip.moveToFront("movePinnedActivityToOriginalTask");
}
}
}
if (creating) {
// Nothing else to do if we don't have a window container yet. E.g. call from ctor.
@@ -7530,7 +7560,11 @@ class Task extends WindowContainer<WindowContainer> {
final Task task = getBottomMostTask();
setWindowingMode(WINDOWING_MODE_UNDEFINED);
getDisplayArea().positionChildAt(POSITION_TOP, this, false /* includingParents */);
// Task could have been removed from the hierarchy due to windowing mode change
// where its only child is reparented back to their original parent task.
if (isAttached()) {
getDisplayArea().positionChildAt(POSITION_TOP, this, false /* includingParents */);
}
mTaskSupervisor.scheduleUpdatePictureInPictureModeIfNeeded(task, this);
});
@@ -7831,6 +7865,7 @@ class Task extends WindowContainer<WindowContainer> {
proto.write(CREATED_BY_ORGANIZER, mCreatedByOrganizer);
proto.write(AFFINITY, affinity);
proto.write(HAS_CHILD_PIP_ACTIVITY, mChildPipActivity != null);
proto.end(token);
}