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

Commit 3e495fb2 authored by Riddle Hsu's avatar Riddle Hsu
Browse files

Reland "Finish sync for activities under a drawn shared starting window"

If a task contains multiple activities and the top one is translucent,
the task may not report sync finish even if the top activity is ready.
Because the activity behind is also visible, then the sync group still
needs to wait for it. But if the snapshot starting window has drawn,
the task should be covered by it. Then all visible activities belonging
to the task can be considered as ready to start transition.

This can also cover the case of hot launch of embedded activities.

About the modification of reland:
Change to check mSyncState instead of isSyncFinished to avoid changing
a finished sync state. Though after [1], the case should be excluded.
Otherwise, the sync transaction may be lost if the container has a sync
state without a sync group.

[1]: Idd1bc09eb9bdec1a8b0ba44fa34f417f2fa92102

Bug: 296817251
Bug: 317833037
Bug: 318136158
Test: atest SyncEngineTests#testFinishSyncByStartingWindow
Test: Activity A starts translucent activity B in the same task
      and the same process. B will call sleep 1s in onResume.
      Return to Home and start the task of A. The transition
      should start in a short time by the starting window.

Change-Id: I2b15f6fd6eb7bfb238de102b5e0d29726d194652
parent 68101dbb
Loading
Loading
Loading
Loading
+16 −1
Original line number Diff line number Diff line
@@ -2495,7 +2495,14 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A

        ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Creating SnapshotStartingData");
        mStartingData = new SnapshotStartingData(mWmService, snapshot, typeParams);
        if (task.forAllLeafTaskFragments(TaskFragment::isEmbedded)) {
        if ((!mStyleFillsParent && task.getChildCount() > 1)
                || task.forAllLeafTaskFragments(TaskFragment::isEmbedded)) {
            // Case 1:
            // If it is moving a Task{[0]=main activity, [1]=translucent activity} to front, use
            // shared starting window so that the transition doesn't need to wait for the activity
            // behind the translucent activity. Also, onFirstWindowDrawn will check all visible
            // activities are drawn in the task to remove the snapshot starting window.
            // Case 2:
            // Associate with the task so if this activity is resized by task fragment later, the
            // starting window can keep the same bounds as the task.
            associateStartingDataWithTask();
@@ -10616,6 +10623,14 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A

    @Override
    boolean isSyncFinished(BLASTSyncEngine.SyncGroup group) {
        if (task != null && task.mSharedStartingData != null) {
            final WindowState startingWin = task.topStartingWindow();
            if (startingWin != null && startingWin.mSyncState == SYNC_STATE_READY
                    && mDisplayContent.mUnknownAppVisibilityController.allResolved()) {
                // The sync is ready if a drawn starting window covered the task.
                return true;
            }
        }
        if (!super.isSyncFinished(group)) return false;
        if (mDisplayContent != null && mDisplayContent.mUnknownAppVisibilityController
                .isVisibilityUnknown(this)) {
+6 −5
Original line number Diff line number Diff line
@@ -1411,12 +1411,13 @@ class Task extends TaskFragment {
        return isUidPresent;
    }

    ActivityRecord topActivityContainsStartingWindow() {
        if (getParent() == null) {
            return null;
    WindowState topStartingWindow() {
        return getWindow(w -> w.mAttrs.type == TYPE_APPLICATION_STARTING);
    }
        return getActivity((r) -> r.getWindow(window ->
                window.getBaseType() == TYPE_APPLICATION_STARTING) != null);

    ActivityRecord topActivityContainsStartingWindow() {
        final WindowState startingWindow = topStartingWindow();
        return startingWindow != null ? startingWindow.mActivityRecord : null;
    }

    /**
+20 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.server.wm;

import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;

import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -144,6 +145,25 @@ public class SyncEngineTests extends WindowTestsBase {
        assertEquals(SYNC_STATE_NONE, r.mSyncState);
    }

    @Test
    public void testFinishSyncByStartingWindow() {
        final ActivityRecord taskRoot = new ActivityBuilder(mAtm).setCreateTask(true).build();
        final Task task = taskRoot.getTask();
        final ActivityRecord translucentTop = new ActivityBuilder(mAtm).setTask(task)
                .setActivityTheme(android.R.style.Theme_Translucent).build();
        createWindow(null, TYPE_BASE_APPLICATION, taskRoot, "win");
        final WindowState startingWindow = createWindow(null, TYPE_APPLICATION_STARTING,
                translucentTop, "starting");
        startingWindow.mStartingData = new SnapshotStartingData(mWm, null, 0);
        task.mSharedStartingData = startingWindow.mStartingData;
        task.prepareSync();

        final BLASTSyncEngine.SyncGroup group = mock(BLASTSyncEngine.SyncGroup.class);
        assertFalse(task.isSyncFinished(group));
        startingWindow.onSyncFinishedDrawing();
        assertTrue(task.isSyncFinished(group));
    }

    @Test
    public void testInvisibleSyncCallback() {
        TestWindowContainer mockWC = new TestWindowContainer(mWm, true /* waiter */);