diff options
5 files changed, 126 insertions, 42 deletions
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java index 872542af0075..dfcad48046b0 100644 --- a/services/core/java/com/android/server/wm/TaskFragment.java +++ b/services/core/java/com/android/server/wm/TaskFragment.java @@ -291,6 +291,12 @@ class TaskFragment extends WindowContainer<WindowContainer> { private final IBinder mFragmentToken; /** + * Whether to delay the call to {@link #updateOrganizedTaskFragmentSurface()} when there is a + * configuration change. + */ + private boolean mDelayOrganizedTaskFragmentSurfaceUpdate; + + /** * Whether to delay the last activity of TaskFragment being immediately removed while finishing. * This should only be set on a embedded TaskFragment, where the organizer can have the * opportunity to perform animations and finishing the adjacent TaskFragment. @@ -2264,35 +2270,41 @@ class TaskFragment extends WindowContainer<WindowContainer> { @Override public void onConfigurationChanged(Configuration newParentConfig) { - // Task will animate differently. - if (mTaskFragmentOrganizer != null) { - mTmpPrevBounds.set(getBounds()); - } - super.onConfigurationChanged(newParentConfig); - final boolean shouldStartChangeTransition = shouldStartChangeTransition(mTmpPrevBounds); - if (shouldStartChangeTransition) { - initializeChangeTransition(mTmpPrevBounds); - } if (mTaskFragmentOrganizer != null) { - if (mTransitionController.isShellTransitionsEnabled() - && !mTransitionController.isCollecting(this)) { - // TaskFragmentOrganizer doesn't have access to the surface for security reasons, so - // update the surface here if it is not collected by Shell transition. - updateOrganizedTaskFragmentSurface(); - } else if (!mTransitionController.isShellTransitionsEnabled() - && !shouldStartChangeTransition) { - // Update the surface here instead of in the organizer so that we can make sure - // it can be synced with the surface freezer for legacy app transition. - updateOrganizedTaskFragmentSurface(); - } + updateOrganizedTaskFragmentSurface(); } sendTaskFragmentInfoChanged(); } + void deferOrganizedTaskFragmentSurfaceUpdate() { + mDelayOrganizedTaskFragmentSurfaceUpdate = true; + } + + void continueOrganizedTaskFragmentSurfaceUpdate() { + mDelayOrganizedTaskFragmentSurfaceUpdate = false; + updateOrganizedTaskFragmentSurface(); + } + private void updateOrganizedTaskFragmentSurface() { + if (mDelayOrganizedTaskFragmentSurfaceUpdate) { + return; + } + if (mTransitionController.isShellTransitionsEnabled() + && !mTransitionController.isCollecting(this)) { + // TaskFragmentOrganizer doesn't have access to the surface for security reasons, so + // update the surface here if it is not collected by Shell transition. + updateOrganizedTaskFragmentSurfaceUnchecked(); + } else if (!mTransitionController.isShellTransitionsEnabled() && !isAnimating()) { + // Update the surface here instead of in the organizer so that we can make sure + // it can be synced with the surface freezer for legacy app transition. + updateOrganizedTaskFragmentSurfaceUnchecked(); + } + } + + private void updateOrganizedTaskFragmentSurfaceUnchecked() { final SurfaceControl.Transaction t = getSyncTransaction(); updateSurfacePosition(t); updateOrganizedTaskFragmentSurfaceSize(t, false /* forceUpdate */); @@ -2346,7 +2358,7 @@ class TaskFragment extends WindowContainer<WindowContainer> { } /** Whether we should prepare a transition for this {@link TaskFragment} bounds change. */ - private boolean shouldStartChangeTransition(Rect startBounds) { + boolean shouldStartChangeTransition(Rect startBounds) { if (mTaskFragmentOrganizer == null || !canStartChangeTransition()) { return false; } @@ -2366,7 +2378,7 @@ class TaskFragment extends WindowContainer<WindowContainer> { void setSurfaceControl(SurfaceControl sc) { super.setSurfaceControl(sc); if (mTaskFragmentOrganizer != null) { - updateOrganizedTaskFragmentSurface(); + updateOrganizedTaskFragmentSurfaceUnchecked(); // If the TaskFragmentOrganizer was set before we created the SurfaceControl, we need to // emit the callbacks now. sendTaskFragmentAppeared(); diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index 5760b77a0be4..de12a4ef7739 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -145,6 +145,8 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub @VisibleForTesting final ArrayMap<IBinder, TaskFragment> mLaunchTaskFragments = new ArrayMap<>(); + private final Rect mTmpBounds = new Rect(); + WindowOrganizerController(ActivityTaskManagerService atm) { mService = atm; mGlobalLock = atm.mGlobalLock; @@ -703,7 +705,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub } private int applyTaskChanges(Task tr, WindowContainerTransaction.Change c) { - int effects = 0; + int effects = applyChanges(tr, c, null /* errorCallbackToken */); final SurfaceControl.Transaction t = c.getBoundsChangeTransaction(); if ((c.getChangeMask() & WindowContainerTransaction.Change.CHANGE_HIDDEN) != 0) { @@ -764,6 +766,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub private int applyDisplayAreaChanges(DisplayArea displayArea, WindowContainerTransaction.Change c) { final int[] effects = new int[1]; + effects[0] = applyChanges(displayArea, c, null /* errorCallbackToken */); if ((c.getChangeMask() & WindowContainerTransaction.Change.CHANGE_IGNORE_ORIENTATION_REQUEST) != 0) { @@ -784,6 +787,27 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub return effects[0]; } + private int applyTaskFragmentChanges(@NonNull TaskFragment taskFragment, + @NonNull WindowContainerTransaction.Change c, @Nullable IBinder errorCallbackToken) { + if (taskFragment.isEmbeddedTaskFragmentInPip()) { + // No override from organizer for embedded TaskFragment in a PIP Task. + return 0; + } + + // When the TaskFragment is resized, we may want to create a change transition for it, for + // which we want to defer the surface update until we determine whether or not to start + // change transition. + mTmpBounds.set(taskFragment.getBounds()); + taskFragment.deferOrganizedTaskFragmentSurfaceUpdate(); + final int effects = applyChanges(taskFragment, c, errorCallbackToken); + if (taskFragment.shouldStartChangeTransition(mTmpBounds)) { + taskFragment.initializeChangeTransition(mTmpBounds); + } + taskFragment.continueOrganizedTaskFragmentSurfaceUpdate(); + mTmpBounds.set(0, 0, 0, 0); + return effects; + } + private int applyHierarchyOp(WindowContainerTransaction.HierarchyOp hop, int effects, int syncId, @Nullable Transition transition, boolean isInLockTaskMode, @NonNull CallerInfo caller, @Nullable IBinder errorCallbackToken, @@ -1449,20 +1473,15 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub private int applyWindowContainerChange(WindowContainer wc, WindowContainerTransaction.Change c, @Nullable IBinder errorCallbackToken) { sanitizeWindowContainer(wc); - if (wc.asTaskFragment() != null && wc.asTaskFragment().isEmbeddedTaskFragmentInPip()) { - // No override from organizer for embedded TaskFragment in a PIP Task. - return 0; - } - - int effects = applyChanges(wc, c, errorCallbackToken); - - if (wc instanceof DisplayArea) { - effects |= applyDisplayAreaChanges(wc.asDisplayArea(), c); - } else if (wc instanceof Task) { - effects |= applyTaskChanges(wc.asTask(), c); + if (wc.asDisplayArea() != null) { + return applyDisplayAreaChanges(wc.asDisplayArea(), c); + } else if (wc.asTask() != null) { + return applyTaskChanges(wc.asTask(), c); + } else if (wc.asTaskFragment() != null) { + return applyTaskFragmentChanges(wc.asTaskFragment(), c, errorCallbackToken); + } else { + return applyChanges(wc, c, errorCallbackToken); } - - return effects; } @Override diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 5c5c70334e65..86dd0b5452b5 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -1540,10 +1540,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mWmService.makeWindowFreezingScreenIfNeededLocked(this); // If the orientation is changing, or we're starting or ending a drag resizing action, - // then we need to hold off on unfreezing the display until this window has been - // redrawn; to do that, we need to go through the process of getting informed by the - // application when it has finished drawing. - if (getOrientationChanging() || dragResizingChanged) { + // or we're resizing an embedded Activity, then we need to hold off on unfreezing the + // display until this window has been redrawn; to do that, we need to go through the + // process of getting informed by the application when it has finished drawing. + if (getOrientationChanging() || dragResizingChanged + || isEmbeddedActivityResizeChanged()) { if (dragResizingChanged) { ProtoLog.v(WM_DEBUG_RESIZE, "Resize start waiting for draw, " @@ -4160,6 +4161,20 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP return mActivityRecord == null || mActivityRecord.isFullyTransparentBarAllowed(frame); } + /** + * Whether this window belongs to a resizing embedded activity. + */ + private boolean isEmbeddedActivityResizeChanged() { + if (mActivityRecord == null || !isVisibleRequested()) { + // No need to update if the window is in the background. + return false; + } + + final TaskFragment embeddedTaskFragment = mActivityRecord.getOrganizedTaskFragment(); + return embeddedTaskFragment != null + && mDisplayContent.mChangingContainers.contains(embeddedTaskFragment); + } + boolean isDragResizeChanged() { return mDragResizing != computeDragResizing(); } diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java index 83f17897eb62..3ff2c0e0d024 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java @@ -118,10 +118,13 @@ public class TaskFragmentTest extends WindowTestsBase { doReturn(true).when(mTaskFragment).isVisibleRequested(); clearInvocations(mTransaction); + mTaskFragment.deferOrganizedTaskFragmentSurfaceUpdate(); mTaskFragment.setBounds(endBounds); + assertTrue(mTaskFragment.shouldStartChangeTransition(startBounds)); + mTaskFragment.initializeChangeTransition(startBounds); + mTaskFragment.continueOrganizedTaskFragmentSurfaceUpdate(); // Surface reset when prepare transition. - verify(mTaskFragment).initializeChangeTransition(startBounds); verify(mTransaction).setPosition(mLeash, 0, 0); verify(mTransaction).setWindowCrop(mLeash, 0, 0); @@ -166,7 +169,7 @@ public class TaskFragmentTest extends WindowTestsBase { mTaskFragment.setBounds(endBounds); - verify(mTaskFragment, never()).initializeChangeTransition(any()); + assertFalse(mTaskFragment.shouldStartChangeTransition(startBounds)); } /** diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java index 6bd341210cad..1b888f629251 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java @@ -95,6 +95,8 @@ import android.view.InsetsSource; import android.view.InsetsState; import android.view.SurfaceControl; import android.view.WindowManager; +import android.window.ITaskFragmentOrganizer; +import android.window.TaskFragmentOrganizer; import androidx.test.filters.SmallTest; @@ -800,6 +802,39 @@ public class WindowStateTests extends WindowTestsBase { } @Test + public void testEmbeddedActivityResizing_clearAllDrawn() { + final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run); + mAtm.mTaskFragmentOrganizerController.registerOrganizer( + ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder())); + final Task task = createTask(mDisplayContent); + final TaskFragment embeddedTf = createTaskFragmentWithEmbeddedActivity(task, organizer); + final ActivityRecord embeddedActivity = embeddedTf.getTopMostActivity(); + final WindowState win = createWindow(null /* parent */, TYPE_APPLICATION, embeddedActivity, + "App window"); + doReturn(true).when(embeddedActivity).isVisible(); + embeddedActivity.mVisibleRequested = true; + makeWindowVisible(win); + win.mLayoutSeq = win.getDisplayContent().mLayoutSeq; + // Set the bounds twice: + // 1. To make sure there is no orientation change after #reportResized, which can also cause + // #clearAllDrawn. + // 2. Make #isLastConfigReportedToClient to be false after #reportResized, so it can process + // to check if we need redraw. + embeddedTf.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); + embeddedTf.setBounds(0, 0, 1000, 2000); + win.reportResized(); + embeddedTf.setBounds(500, 0, 1000, 2000); + + // Clear all drawn when the embedded TaskFragment is in mDisplayContent.mChangingContainers. + win.updateResizingWindowIfNeeded(); + verify(embeddedActivity, never()).clearAllDrawn(); + + mDisplayContent.mChangingContainers.add(embeddedTf); + win.updateResizingWindowIfNeeded(); + verify(embeddedActivity).clearAllDrawn(); + } + + @Test public void testCantReceiveTouchWhenAppTokenHiddenRequested() { final WindowState win0 = createWindow(null, TYPE_APPLICATION, "win0"); win0.mActivityRecord.mVisibleRequested = false; |