diff options
7 files changed, 87 insertions, 9 deletions
diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 0541934fd665..a77bf32544e4 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -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(); diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java index 627017ce0893..28d6fbb36a47 100644 --- a/core/java/android/app/ActivityTaskManager.java +++ b/core/java/android/app/ActivityTaskManager.java @@ -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); diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto index a7127ad79401..b157146e70a3 100644 --- a/core/proto/android/server/windowmanagerservice.proto +++ b/core/proto/android/server/windowmanagerservice.proto @@ -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 */ diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index f5cfc9d86cf4..a3624e014e30 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -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; diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java index 6631a3e61e77..4bb48b0bdbe8 100644 --- a/services/core/java/com/android/server/wm/RecentTasks.java +++ b/services/core/java/com/android/server/wm/RecentTasks.java @@ -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; diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 52551ec54b32..b9fc8b1222ee 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -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. diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 86904991616f..07f81c725d4d 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -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); } |