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 Diff line number Diff line
@@ -280,6 +280,9 @@ class TaskFragmentContainer {
     * Adds a container that should be finished when this container is finished.
     */
    void addContainerToFinishOnExit(@NonNull TaskFragmentContainer containerToFinish) {
        if (mIsFinished) {
            return;
        }
        mContainersToFinishOnExit.add(containerToFinish);
    }

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

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

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

    /** Removes all dependencies that should be finished when this container is finished. */
    void resetDependencies() {
        if (mIsFinished) {
            return;
        }
        mContainersToFinishOnExit.clear();
        mActivitiesToFinishOnExit.clear();
    }
+64 −8
Original line number 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_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.verify;

@@ -89,6 +92,10 @@ public class SplitControllerTest {
    private static final Intent PLACEHOLDER_INTENT = new Intent().setComponent(
            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;
    @Mock
    private Resources mActivityResources;
@@ -788,6 +795,38 @@ public class SplitControllerTest {
        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. */
    private Activity createMockActivity() {
        final Activity activity = mock(Activity.class);
@@ -863,7 +902,9 @@ public class SplitControllerTest {
    /** Setups a rule to always split the given activities. */
    private void setupSplitRule(@NonNull Activity primaryActivity,
            @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));
    }

@@ -883,29 +924,44 @@ public class SplitControllerTest {
    /** Creates a rule to always split the given activities. */
    private SplitRule createSplitRule(@NonNull Activity primaryActivity,
            @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);
        return new SplitPairRule.Builder(
                targetPair::equals,
                activityIntentPair -> false,
                w -> true)
                .setSplitRatio(SPLIT_RATIO)
                .setShouldClearTop(true)
                .setFinishPrimaryWithSecondary(finishPrimaryWithSecondary)
                .setFinishSecondaryWithPrimary(finishSecondaryWithPrimary)
                .setShouldClearTop(clearTop)
                .build();
    }

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

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

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