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

Commit 026d5951 authored by Evan Rosky's avatar Evan Rosky
Browse files

Don't abort transition for activity re-ordering.

ActivityStarter will reorder activities and change their
visibilities but still report DELIVERED_TO_TOP. This means
we need to do some secondary checks for whether the top
changed. This adds some additional tracking so that we
don't abort the transition if activity visibility does
change.

Bug: 243812111
Test: wmtest ActivityStarterTests
Change-Id: I14e42b55cc95b9331d1dbc1afb5da1457acedf9d
parent 54dd46a8
Loading
Loading
Loading
Loading
+18 −3
Original line number Diff line number Diff line
@@ -203,6 +203,10 @@ class ActivityStarter {
    private TaskFragment mAddingToTaskFragment;
    @VisibleForTesting
    boolean mAddingToTask;
    // Activity that was moved to the top of its task in situations where activity-order changes
    // due to launch flags (eg. REORDER_TO_TOP).
    @VisibleForTesting
    ActivityRecord mMovedToTopActivity;

    private ActivityInfo mNewTaskInfo;
    private Intent mNewTaskIntent;
@@ -1763,7 +1767,9 @@ class ActivityStarter {
            // The activity is started new rather than just brought forward, so record it as an
            // existence change.
            transitionController.collectExistenceChange(started);
        } else if (result == START_DELIVERED_TO_TOP && newTransition != null) {
        } else if (result == START_DELIVERED_TO_TOP && newTransition != null
                // An activity has changed order/visibility so this isn't just deliver-to-top
                && mMovedToTopActivity == null) {
            // We just delivered to top, so there isn't an actual transition here.
            if (!forceTransientTransition) {
                newTransition.abort();
@@ -2343,10 +2349,15 @@ class ActivityStarter {
            // In this situation we want to remove all activities from the task up to the one
            // being started. In most cases this means we are resetting the task to its initial
            // state.
            int[] finishCount = new int[1];
            final ActivityRecord clearTop = targetTask.performClearTop(mStartActivity,
                    mLaunchFlags);
                    mLaunchFlags, finishCount);

            if (clearTop != null && !clearTop.finishing) {
                if (finishCount[0] > 0) {
                    // Only record if actually moved to top.
                    mMovedToTopActivity = clearTop;
                }
                if (clearTop.isRootOfTask()) {
                    // Activity aliases may mean we use different intents for the top activity,
                    // so make sure the task now has the identity of the new intent.
@@ -2383,7 +2394,11 @@ class ActivityStarter {
                            mStartActivity.mUserId);
            if (act != null) {
                final Task task = act.getTask();
                task.moveActivityToFrontLocked(act);
                boolean actuallyMoved = task.moveActivityToFrontLocked(act);
                if (actuallyMoved) {
                    // Only record if the activity actually moved.
                    mMovedToTopActivity = act;
                }
                act.updateOptionsLocked(mOptions);
                deliverNewIntent(act, intentGrants);
                act.getTaskFragment().clearLastPausedActivity();
+16 −7
Original line number Diff line number Diff line
@@ -1399,13 +1399,15 @@ class Task extends TaskFragment {

    /**
     * Reorder the history task so that the passed activity is brought to the front.
     * @return whether it was actually moved (vs already being top).
     */
    final void moveActivityToFrontLocked(ActivityRecord newTop) {
    final boolean moveActivityToFrontLocked(ActivityRecord newTop) {
        ProtoLog.i(WM_DEBUG_ADD_REMOVE, "Removing and adding activity %s to root task at top "
                + "callers=%s", newTop, Debug.getCallers(4));

        int origDist = getDistanceFromTop(newTop);
        positionChildAtTop(newTop);
        updateEffectiveIntent();
        return getDistanceFromTop(newTop) != origDist;
    }

    @Override
@@ -1613,14 +1615,14 @@ class Task extends TaskFragment {
        }
    }

    ActivityRecord performClearTop(ActivityRecord newR, int launchFlags) {
    ActivityRecord performClearTop(ActivityRecord newR, int launchFlags, int[] finishCount) {
        // The task should be preserved for putting new activity in case the last activity is
        // finished if it is normal launch mode and not single top ("clear-task-top").
        mReuseTask = true;
        mTaskSupervisor.beginDeferResume();
        final ActivityRecord result;
        try {
            result = clearTopActivities(newR, launchFlags);
            result = clearTopActivities(newR, launchFlags, finishCount);
        } finally {
            mTaskSupervisor.endDeferResume();
            mReuseTask = false;
@@ -1636,14 +1638,19 @@ class Task extends TaskFragment {
     * activities on top of it and return the instance.
     *
     * @param newR Description of the new activity being started.
     * @param finishCount 1-element array that will be populated with the number of activities
     *                    that have been finished.
     * @return Returns the existing activity in the task that performs the clear-top operation,
     * or {@code null} if none was found.
     */
    private ActivityRecord clearTopActivities(ActivityRecord newR, int launchFlags) {
    private ActivityRecord clearTopActivities(ActivityRecord newR, int launchFlags,
            int[] finishCount) {
        final ActivityRecord r = findActivityInHistory(newR.mActivityComponent, newR.mUserId);
        if (r == null) return null;

        final PooledPredicate f = PooledLambda.obtainPredicate(Task::finishActivityAbove,
        final PooledPredicate f = PooledLambda.obtainPredicate(
                (ActivityRecord ar, ActivityRecord boundaryActivity) ->
                        finishActivityAbove(ar, boundaryActivity, finishCount),
                PooledLambda.__(ActivityRecord.class), r);
        forAllActivities(f);
        f.recycle();
@@ -1661,7 +1668,8 @@ class Task extends TaskFragment {
        return r;
    }

    private static boolean finishActivityAbove(ActivityRecord r, ActivityRecord boundaryActivity) {
    private static boolean finishActivityAbove(ActivityRecord r, ActivityRecord boundaryActivity,
            @NonNull int[] finishCount) {
        // Stop operation once we reach the boundary activity.
        if (r == boundaryActivity) return true;

@@ -1672,6 +1680,7 @@ class Task extends TaskFragment {
                // TODO: Why is this updating the boundary activity vs. the current activity???
                boundaryActivity.updateOptionsLocked(opts);
            }
            finishCount[0] += 1;
            r.finishIfPossible("clear-task-stack", false /* oomAdj */);
        }

+5 −0
Original line number Diff line number Diff line
@@ -1834,6 +1834,11 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
        return null;
    }

    int getDistanceFromTop(WindowContainer child) {
        int idx = mChildren.indexOf(child);
        return idx < 0 ? -1 : mChildren.size() - 1 - idx;
    }

    private ActivityRecord processGetActivityWithBoundary(Predicate<ActivityRecord> callback,
            WindowContainer boundary, boolean includeBoundary, boolean traverseTopToBottom,
            boolean[] boundaryFound, WindowContainer wc) {
+35 −0
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
import static android.content.Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.Intent.FLAG_ACTIVITY_REORDER_TO_FRONT;
import static android.content.Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED;
import static android.content.Intent.FLAG_ACTIVITY_SINGLE_TOP;
import static android.content.pm.ActivityInfo.FLAG_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING;
@@ -66,6 +67,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -1402,6 +1404,39 @@ public class ActivityStarterTests extends WindowTestsBase {
                canEmbedActivity(taskFragment, starting, task));
    }

    @Test
    public void testRecordActivityMovementBeforeDeliverToTop() {
        final Task task = new TaskBuilder(mAtm.mTaskSupervisor).build();
        final ActivityRecord activityBot = new ActivityBuilder(mAtm).setTask(task).build();
        final ActivityRecord activityTop = new ActivityBuilder(mAtm).setTask(task).build();

        activityBot.setVisible(false);
        activityBot.mVisibleRequested = false;

        assertTrue(activityTop.isVisible());
        assertTrue(activityTop.mVisibleRequested);

        final ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_REORDER_TO_FRONT
                        | FLAG_ACTIVITY_NEW_TASK, false /* mockGetRootTask */);
        starter.mStartActivity = activityBot;
        task.inRecents = true;
        starter.setInTask(task);
        starter.getIntent().setComponent(activityBot.mActivityComponent);
        final int result = starter.setReason("testRecordActivityMovement").execute();

        assertEquals(START_DELIVERED_TO_TOP, result);
        assertNotNull(starter.mMovedToTopActivity);

        final ActivityStarter starter2 = prepareStarter(FLAG_ACTIVITY_REORDER_TO_FRONT
                        | FLAG_ACTIVITY_NEW_TASK, false /* mockGetRootTask */);
        starter2.setInTask(task);
        starter2.getIntent().setComponent(activityBot.mActivityComponent);
        final int result2 = starter2.setReason("testRecordActivityMovement").execute();

        assertEquals(START_DELIVERED_TO_TOP, result2);
        assertNull(starter2.mMovedToTopActivity);
    }

    private static void startActivityInner(ActivityStarter starter, ActivityRecord target,
            ActivityRecord source, ActivityOptions options, Task inTask,
            TaskFragment inTaskFragment) {
+3 −2
Original line number Diff line number Diff line
@@ -263,7 +263,8 @@ public class TaskTests extends WindowTestsBase {
        // Detach from process so the activities can be removed from hierarchy when finishing.
        activity1.detachFromProcess();
        activity2.detachFromProcess();
        assertTrue(task.performClearTop(activity1, 0 /* launchFlags */).finishing);
        int[] finishCount = new int[1];
        assertTrue(task.performClearTop(activity1, 0 /* launchFlags */, finishCount).finishing);
        assertFalse(task.hasChild());
        // In real case, the task should be preserved for adding new activity.
        assertTrue(task.isAttached());
@@ -277,7 +278,7 @@ public class TaskTests extends WindowTestsBase {
        doReturn(true).when(activityB).shouldBeVisibleUnchecked();
        doReturn(true).when(activityC).shouldBeVisibleUnchecked();
        activityA.getConfiguration().densityDpi += 100;
        assertTrue(task.performClearTop(activityA, 0 /* launchFlags */).finishing);
        assertTrue(task.performClearTop(activityA, 0 /* launchFlags */, finishCount).finishing);
        // The bottom activity should destroy directly without relaunch for config change.
        assertEquals(ActivityRecord.State.DESTROYING, activityA.getState());
        verify(activityA, never()).startRelaunching();