Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 99604f0f authored by Chris Li's avatar Chris Li
Browse files

Fix ActivityEmbedding animation flicker

1. Fix flicker when rotate display with expanded TaskFragment:
   Only startChangeTransition if organizer request resizing, so we won't
   accidentally start the change transition for other resizing case,
   such as display rotation.
2. Fix flciker when exit split to fullscreen:
   Make sure to wait activity window redraw when the embedded
   TaskFragment is changing.

Fix: 258755905
Test: manual verify open fullscreen fixed-portrait embedded activity
      from landscape display.
Test: atest WmTests:WindowStateTests
Change-Id: Ifc825075abfb7c27461f23281f41425f66c3f680
parent 32c99955
Loading
Loading
Loading
Loading
+34 −22
Original line number Diff line number Diff line
@@ -288,6 +288,12 @@ class TaskFragment extends WindowContainer<WindowContainer> {
    @Nullable
    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
@@ -2273,35 +2279,41 @@ class TaskFragment extends WindowContainer<WindowContainer> {

    @Override
    public void onConfigurationChanged(Configuration newParentConfig) {
        // Task will animate differently.
        super.onConfigurationChanged(newParentConfig);

        if (mTaskFragmentOrganizer != null) {
            mTmpPrevBounds.set(getBounds());
            updateOrganizedTaskFragmentSurface();
        }

        super.onConfigurationChanged(newParentConfig);
        sendTaskFragmentInfoChanged();
    }

        final boolean shouldStartChangeTransition = shouldStartChangeTransition(mTmpPrevBounds);
        if (shouldStartChangeTransition) {
            initializeChangeTransition(mTmpPrevBounds);
    void deferOrganizedTaskFragmentSurfaceUpdate() {
        mDelayOrganizedTaskFragmentSurfaceUpdate = true;
    }

    void continueOrganizedTaskFragmentSurfaceUpdate() {
        mDelayOrganizedTaskFragmentSurfaceUpdate = false;
        updateOrganizedTaskFragmentSurface();
    }

    private void updateOrganizedTaskFragmentSurface() {
        if (mDelayOrganizedTaskFragmentSurfaceUpdate) {
            return;
        }
        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) {
            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.
                updateOrganizedTaskFragmentSurface();
            }
            updateOrganizedTaskFragmentSurfaceUnchecked();
        }

        sendTaskFragmentInfoChanged();
    }

    private void updateOrganizedTaskFragmentSurface() {
    private void updateOrganizedTaskFragmentSurfaceUnchecked() {
        final SurfaceControl.Transaction t = getSyncTransaction();
        updateSurfacePosition(t);
        updateOrganizedTaskFragmentSurfaceSize(t, false /* forceUpdate */);
@@ -2355,7 +2367,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;
        }
@@ -2375,7 +2387,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();
+33 −14
Original line number Diff line number Diff line
@@ -146,6 +146,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;
@@ -710,7 +712,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) {
@@ -767,6 +769,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) {
@@ -787,6 +790,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,
@@ -1452,20 +1476,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
+19 −4
Original line number Diff line number Diff line
@@ -1535,10 +1535,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, "
@@ -4144,6 +4145,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();
    }
+5 −2
Original line number Diff line number Diff line
@@ -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));
    }

    /**
+35 −0
Original line number Diff line number Diff line
@@ -95,6 +95,8 @@ import android.view.InsetsState;
import android.view.InsetsVisibilities;
import android.view.SurfaceControl;
import android.view.WindowManager;
import android.window.ITaskFragmentOrganizer;
import android.window.TaskFragmentOrganizer;

import androidx.test.filters.SmallTest;

@@ -797,6 +799,39 @@ public class WindowStateTests extends WindowTestsBase {
        assertThat(mWm.mResizingWindows).doesNotContain(win);
    }

    @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");