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

Commit 7be02ab8 authored by Chris Li's avatar Chris Li Committed by Automerger Merge Worker
Browse files

Merge "Fix ConcurrentModificationException on finishing TaskFragment" into...

Merge "Fix ConcurrentModificationException on finishing TaskFragment" into tm-dev am: 0d7ece81 am: 91b3e6b4 am: 7c2912df

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/18615113



Change-Id: If1c24e133899dc8beed191c8ddfe7c5bd5f8e518
Signed-off-by: default avatarAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
parents 9d73e282 7c2912df
Loading
Loading
Loading
Loading
+15 −0
Original line number Original line Diff line number Diff line
@@ -280,6 +280,9 @@ class TaskFragmentContainer {
     * Adds a container that should be finished when this container is finished.
     * Adds a container that should be finished when this container is finished.
     */
     */
    void addContainerToFinishOnExit(@NonNull TaskFragmentContainer containerToFinish) {
    void addContainerToFinishOnExit(@NonNull TaskFragmentContainer containerToFinish) {
        if (mIsFinished) {
            return;
        }
        mContainersToFinishOnExit.add(containerToFinish);
        mContainersToFinishOnExit.add(containerToFinish);
    }
    }


@@ -287,6 +290,9 @@ class TaskFragmentContainer {
     * Removes a container that should be finished when this container is finished.
     * Removes a container that should be finished when this container is finished.
     */
     */
    void removeContainerToFinishOnExit(@NonNull TaskFragmentContainer containerToRemove) {
    void removeContainerToFinishOnExit(@NonNull TaskFragmentContainer containerToRemove) {
        if (mIsFinished) {
            return;
        }
        mContainersToFinishOnExit.remove(containerToRemove);
        mContainersToFinishOnExit.remove(containerToRemove);
    }
    }


@@ -294,6 +300,9 @@ class TaskFragmentContainer {
     * Adds an activity that should be finished when this container is finished.
     * Adds an activity that should be finished when this container is finished.
     */
     */
    void addActivityToFinishOnExit(@NonNull Activity activityToFinish) {
    void addActivityToFinishOnExit(@NonNull Activity activityToFinish) {
        if (mIsFinished) {
            return;
        }
        mActivitiesToFinishOnExit.add(activityToFinish);
        mActivitiesToFinishOnExit.add(activityToFinish);
    }
    }


@@ -301,11 +310,17 @@ class TaskFragmentContainer {
     * Removes an activity that should be finished when this container is finished.
     * Removes an activity that should be finished when this container is finished.
     */
     */
    void removeActivityToFinishOnExit(@NonNull Activity activityToRemove) {
    void removeActivityToFinishOnExit(@NonNull Activity activityToRemove) {
        if (mIsFinished) {
            return;
        }
        mActivitiesToFinishOnExit.remove(activityToRemove);
        mActivitiesToFinishOnExit.remove(activityToRemove);
    }
    }


    /** Removes all dependencies that should be finished when this container is finished. */
    /** Removes all dependencies that should be finished when this container is finished. */
    void resetDependencies() {
    void resetDependencies() {
        if (mIsFinished) {
            return;
        }
        mContainersToFinishOnExit.clear();
        mContainersToFinishOnExit.clear();
        mActivitiesToFinishOnExit.clear();
        mActivitiesToFinishOnExit.clear();
    }
    }
+64 −8
Original line number Original line Diff line number Diff line
@@ -19,6 +19,9 @@ package androidx.window.extensions.embedding;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;


import static androidx.window.extensions.embedding.SplitRule.FINISH_ALWAYS;
import static androidx.window.extensions.embedding.SplitRule.FINISH_NEVER;

import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;


@@ -89,6 +92,10 @@ public class SplitControllerTest {
    private static final Intent PLACEHOLDER_INTENT = new Intent().setComponent(
    private static final Intent PLACEHOLDER_INTENT = new Intent().setComponent(
            new ComponentName("test", "placeholder"));
            new ComponentName("test", "placeholder"));


    /** Default finish behavior in Jetpack. */
    private static final int DEFAULT_FINISH_PRIMARY_WITH_SECONDARY = FINISH_NEVER;
    private static final int DEFAULT_FINISH_SECONDARY_WITH_PRIMARY = FINISH_ALWAYS;

    private Activity mActivity;
    private Activity mActivity;
    @Mock
    @Mock
    private Resources mActivityResources;
    private Resources mActivityResources;
@@ -788,6 +795,38 @@ public class SplitControllerTest {
        assertTrue(activityOptions.getAvoidMoveToFront());
        assertTrue(activityOptions.getAvoidMoveToFront());
    }
    }


    @Test
    public void testFinishTwoSplitThatShouldFinishTogether() {
        // Setup two split pairs that should finish each other when finishing one.
        final Activity secondaryActivity0 = createMockActivity();
        final Activity secondaryActivity1 = createMockActivity();
        final TaskFragmentContainer primaryContainer = createMockTaskFragmentContainer(mActivity);
        final TaskFragmentContainer secondaryContainer0 = createMockTaskFragmentContainer(
                secondaryActivity0);
        final TaskFragmentContainer secondaryContainer1 = createMockTaskFragmentContainer(
                secondaryActivity1);
        final TaskContainer taskContainer = mSplitController.getTaskContainer(TASK_ID);
        final SplitRule rule0 = createSplitRule(mActivity, secondaryActivity0, FINISH_ALWAYS,
                FINISH_ALWAYS, false /* clearTop */);
        final SplitRule rule1 = createSplitRule(mActivity, secondaryActivity1, FINISH_ALWAYS,
                FINISH_ALWAYS, false /* clearTop */);
        registerSplitPair(primaryContainer, secondaryContainer0, rule0);
        registerSplitPair(primaryContainer, secondaryContainer1, rule1);

        primaryContainer.finish(true /* shouldFinishDependent */, mSplitPresenter,
                mTransaction, mSplitController);

        // All containers and activities should be finished based on the FINISH_ALWAYS behavior.
        assertTrue(primaryContainer.isFinished());
        assertTrue(secondaryContainer0.isFinished());
        assertTrue(secondaryContainer1.isFinished());
        verify(mActivity).finish();
        verify(secondaryActivity0).finish();
        verify(secondaryActivity1).finish();
        assertTrue(taskContainer.mContainers.isEmpty());
        assertTrue(taskContainer.mSplitContainers.isEmpty());
    }

    /** Creates a mock activity in the organizer process. */
    /** Creates a mock activity in the organizer process. */
    private Activity createMockActivity() {
    private Activity createMockActivity() {
        final Activity activity = mock(Activity.class);
        final Activity activity = mock(Activity.class);
@@ -863,7 +902,9 @@ public class SplitControllerTest {
    /** Setups a rule to always split the given activities. */
    /** Setups a rule to always split the given activities. */
    private void setupSplitRule(@NonNull Activity primaryActivity,
    private void setupSplitRule(@NonNull Activity primaryActivity,
            @NonNull Activity secondaryActivity) {
            @NonNull Activity secondaryActivity) {
        final SplitRule splitRule = createSplitRule(primaryActivity, secondaryActivity);
        final SplitRule splitRule = createSplitRule(primaryActivity, secondaryActivity,
                DEFAULT_FINISH_PRIMARY_WITH_SECONDARY, DEFAULT_FINISH_SECONDARY_WITH_PRIMARY,
                true /* clearTop */);
        mSplitController.setEmbeddingRules(Collections.singleton(splitRule));
        mSplitController.setEmbeddingRules(Collections.singleton(splitRule));
    }
    }


@@ -883,29 +924,44 @@ public class SplitControllerTest {
    /** Creates a rule to always split the given activities. */
    /** Creates a rule to always split the given activities. */
    private SplitRule createSplitRule(@NonNull Activity primaryActivity,
    private SplitRule createSplitRule(@NonNull Activity primaryActivity,
            @NonNull Activity secondaryActivity) {
            @NonNull Activity secondaryActivity) {
        return createSplitRule(primaryActivity, secondaryActivity,
                DEFAULT_FINISH_PRIMARY_WITH_SECONDARY, DEFAULT_FINISH_SECONDARY_WITH_PRIMARY,
                true /* clearTop */);
    }

    /** Creates a rule to always split the given activities with the given finish behaviors. */
    private SplitRule createSplitRule(@NonNull Activity primaryActivity,
            @NonNull Activity secondaryActivity, int finishPrimaryWithSecondary,
            int finishSecondaryWithPrimary, boolean clearTop) {
        final Pair<Activity, Activity> targetPair = new Pair<>(primaryActivity, secondaryActivity);
        final Pair<Activity, Activity> targetPair = new Pair<>(primaryActivity, secondaryActivity);
        return new SplitPairRule.Builder(
        return new SplitPairRule.Builder(
                targetPair::equals,
                targetPair::equals,
                activityIntentPair -> false,
                activityIntentPair -> false,
                w -> true)
                w -> true)
                .setSplitRatio(SPLIT_RATIO)
                .setSplitRatio(SPLIT_RATIO)
                .setShouldClearTop(true)
                .setFinishPrimaryWithSecondary(finishPrimaryWithSecondary)
                .setFinishSecondaryWithPrimary(finishSecondaryWithPrimary)
                .setShouldClearTop(clearTop)
                .build();
                .build();
    }
    }


    /** Adds a pair of TaskFragments as split for the given activities. */
    /** Adds a pair of TaskFragments as split for the given activities. */
    private void addSplitTaskFragments(@NonNull Activity primaryActivity,
    private void addSplitTaskFragments(@NonNull Activity primaryActivity,
            @NonNull Activity secondaryActivity) {
            @NonNull Activity secondaryActivity) {
        final TaskFragmentContainer primaryContainer = createMockTaskFragmentContainer(
        registerSplitPair(createMockTaskFragmentContainer(primaryActivity),
                primaryActivity);
                createMockTaskFragmentContainer(secondaryActivity),
        final TaskFragmentContainer secondaryContainer = createMockTaskFragmentContainer(
                createSplitRule(primaryActivity, secondaryActivity));
                secondaryActivity);
    }

    /** Registers the two given TaskFragments as split pair. */
    private void registerSplitPair(@NonNull TaskFragmentContainer primaryContainer,
            @NonNull TaskFragmentContainer secondaryContainer, @NonNull SplitRule rule) {
        mSplitController.registerSplit(
        mSplitController.registerSplit(
                mock(WindowContainerTransaction.class),
                mock(WindowContainerTransaction.class),
                primaryContainer,
                primaryContainer,
                primaryActivity,
                primaryContainer.getTopNonFinishingActivity(),
                secondaryContainer,
                secondaryContainer,
                createSplitRule(primaryActivity, secondaryActivity));
                rule);


        // We need to set those in case we are not respecting clear top.
        // We need to set those in case we are not respecting clear top.
        // TODO(b/231845476) we should always respect clearTop.
        // TODO(b/231845476) we should always respect clearTop.