Merge "Add ActivityEmbeddingLaunchTests#testPrimaryActivityLaunchToSideClearTop" into sc-v2-dev
diff --git a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingLaunchTests.java b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingLaunchTests.java
index eae808a..b5c1d3f7 100644
--- a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingLaunchTests.java
+++ b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingLaunchTests.java
@@ -22,6 +22,8 @@
 import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.createWildcardSplitPairRule;
 import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.startActivityAndVerifySplit;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 import static org.junit.Assume.assumeNotNull;
 
 import android.app.Activity;
@@ -29,7 +31,7 @@
 import android.server.wm.jetpack.utils.WindowManagerJetpackTestBase;
 import android.server.wm.jetpack.utils.TestActivityWithId;
 import android.server.wm.jetpack.utils.TestConfigChangeHandlingActivity;
-import android.server.wm.jetpack.utils.TestFirstValueConsumer;
+import android.server.wm.jetpack.utils.TestValueCountConsumer;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.window.extensions.WindowExtensions;
@@ -56,7 +58,7 @@
 public class ActivityEmbeddingLaunchTests extends WindowManagerJetpackTestBase {
 
     private ActivityEmbeddingComponent mActivityEmbeddingComponent;
-    private TestFirstValueConsumer<List<SplitInfo>> mSplitInfoConsumer;
+    private TestValueCountConsumer<List<SplitInfo>> mSplitInfoConsumer;
 
     @Before
     public void setUp() {
@@ -71,7 +73,7 @@
             }
         });
         assumeNotNull(mActivityEmbeddingComponent);
-        mSplitInfoConsumer = new TestFirstValueConsumer<>();
+        mSplitInfoConsumer = new TestValueCountConsumer<>();
         mActivityEmbeddingComponent.setSplitInfoCallback(mSplitInfoConsumer);
     }
 
@@ -89,8 +91,54 @@
         // all successfully split with the primary activity.
         final int numActivitiesToLaunch = 4;
         for (int i = 0; i < numActivitiesToLaunch; i++) {
-            Activity secondaryActivity = startActivityAndVerifySplit(mSplitInfoConsumer,
-                    primaryActivity, TestActivityWithId.class, splitPairRule, Integer.toString(i));
+            Activity secondaryActivity = startActivityAndVerifySplit(primaryActivity,
+                    TestActivityWithId.class, splitPairRule,
+                    Integer.toString(i) /* secondActivityId */, mSplitInfoConsumer);
         }
     }
+
+    /**
+     * Tests launching activities to the side from the primary activity where the secondary stack
+     * is cleared after each launch.
+     */
+    @Test
+    public void testPrimaryActivityLaunchToSideClearTop() {
+        Activity primaryActivity = startActivityNewTask(TestConfigChangeHandlingActivity.class);
+
+        SplitPairRule splitPairRule = createWildcardSplitPairRule(true /* shouldClearTop */);
+        mActivityEmbeddingComponent.setEmbeddingRules(Collections.singleton(splitPairRule));
+
+        Activity secondaryActivity = startActivityAndVerifySplit(primaryActivity,
+                TestActivityWithId.class, splitPairRule,
+                "initialSecondaryActivity" /* secondActivityId */, mSplitInfoConsumer);
+
+        // Launch multiple activities to the side from the primary activity and verify that they
+        // all successfully split with the primary activity and that the previous secondary activity
+        // is finishing.
+        final int numActivitiesToLaunch = 4;
+        Activity prevSecondaryActivity;
+        for (int i = 0; i < numActivitiesToLaunch; i++) {
+            prevSecondaryActivity = secondaryActivity;
+            // Expect the split info consumer to return a value after the 3rd callback because the
+            // 1st callback will return empty split states due to clearing the previous secondary
+            // container, the 2nd callback will return a non-empty primary container with an empty
+            // secondary container because the primary container was just registered, and finally
+            // the 3rd callback will contain the secondary activity in the secondary container.
+            secondaryActivity = startActivityAndVerifySplit(primaryActivity,
+                    TestActivityWithId.class, splitPairRule,
+                    Integer.toString(i) /* secondActivityId */, mSplitInfoConsumer,
+                    3 /* expectedCallbackCount */);
+            // The previous secondary activity should be finishing because shouldClearTop was set
+            // to true, which clears the secondary container before launching the next secondary
+            // activity.
+            assertTrue(prevSecondaryActivity.isFinishing());
+        }
+
+        // Verify that the last reported split info only contains the final split
+        final List<SplitInfo> lastReportedSplitInfo = mSplitInfoConsumer.getLastReportedValue();
+        assertEquals(1, lastReportedSplitInfo.size());
+        final SplitInfo splitInfo = lastReportedSplitInfo.get(0);
+        assertEquals(1, splitInfo.getPrimaryActivityStack().getActivities().size());
+        assertEquals(1, splitInfo.getSecondaryActivityStack().getActivities().size());
+    }
 }
diff --git a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ExtensionWindowLayoutComponentTest.java b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ExtensionWindowLayoutComponentTest.java
index 0ff4fa3..d8c804f 100644
--- a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ExtensionWindowLayoutComponentTest.java
+++ b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ExtensionWindowLayoutComponentTest.java
@@ -58,7 +58,7 @@
 import android.server.wm.jetpack.utils.WindowManagerJetpackTestBase;
 import android.server.wm.jetpack.utils.TestActivity;
 import android.server.wm.jetpack.utils.TestConfigChangeHandlingActivity;
-import android.server.wm.jetpack.utils.TestFirstValueConsumer;
+import android.server.wm.jetpack.utils.TestValueCountConsumer;
 import android.platform.test.annotations.Presubmit;
 
 import androidx.annotation.NonNull;
@@ -113,8 +113,8 @@
 
     @Test
     public void testWindowLayoutComponent_WindowLayoutInfoListener() {
-        TestFirstValueConsumer<WindowLayoutInfo> windowLayoutInfoConsumer =
-                new TestFirstValueConsumer<>();
+        TestValueCountConsumer<WindowLayoutInfo> windowLayoutInfoConsumer =
+                new TestValueCountConsumer<>();
         // Test that adding and removing callback succeeds
         mWindowLayoutComponent.addWindowLayoutInfoListener(mActivity, windowLayoutInfoConsumer);
         mWindowLayoutComponent.removeWindowLayoutInfoListener(windowLayoutInfoConsumer);
diff --git a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/ActivityEmbeddingUtil.java b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/ActivityEmbeddingUtil.java
index 4f8e118..fbb2a31 100644
--- a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/ActivityEmbeddingUtil.java
+++ b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/ActivityEmbeddingUtil.java
@@ -70,7 +70,7 @@
     public static final float DEFAULT_SPLIT_RATIO = 0.5f;
 
     @NonNull
-    public static SplitPairRule createWildcardSplitPairRule() {
+    public static SplitPairRule createWildcardSplitPairRule(boolean shouldClearTop) {
         // Any activity be split with any activity
         final Predicate<Pair<Activity, Activity>> activityPairPredicate =
                 activityActivityPair -> true;
@@ -82,14 +82,21 @@
         // Build the split pair rule
         return new SplitPairRule.Builder(activityPairPredicate,
                 activityIntentPredicate, parentWindowMetricsPredicate).setSplitRatio(
-                DEFAULT_SPLIT_RATIO).build();
+                DEFAULT_SPLIT_RATIO).setShouldClearTop(shouldClearTop).build();
     }
 
-    public static Activity startActivityAndVerifySplit(
-            TestFirstValueConsumer<List<SplitInfo>> splitInfoConsumer, Activity primaryActivity,
-            Class secondActivityClass, SplitPairRule splitPairRule, String secondActivityId) {
-        // Must reset consumer so that most recent split info update can be received
-        splitInfoConsumer.reset();
+    @NonNull
+    public static SplitPairRule createWildcardSplitPairRule() {
+        return createWildcardSplitPairRule(false /* shouldClearTop */);
+    }
+
+    public static Activity startActivityAndVerifySplit(@NonNull Activity primaryActivity,
+            @NonNull Class secondActivityClass, @NonNull SplitPairRule splitPairRule,
+            @NonNull String secondActivityId,
+            @NonNull TestValueCountConsumer<List<SplitInfo>> splitInfoConsumer,
+            int expectedCallbackCount) {
+        // Set the expected callback count
+        splitInfoConsumer.setCount(expectedCallbackCount);
 
         // Start second activity
         startActivityFromActivity(primaryActivity, secondActivityClass, secondActivityId);
@@ -100,10 +107,6 @@
             activeSplitStates = splitInfoConsumer.waitAndGet();
         } catch (InterruptedException e) {
             fail("startActivityAndVerifySplit() InterruptedException");
-        } catch (ExecutionException e) {
-            fail("startActivityAndVerifySplit() ExecutionException");
-        } catch (TimeoutException e) {
-            fail("startActivityAndVerifySplit() TimeoutException");
         }
 
         // Get second activity from split info
@@ -117,6 +120,14 @@
         return secondActivity;
     }
 
+    public static Activity startActivityAndVerifySplit(@NonNull Activity primaryActivity,
+            @NonNull Class secondActivityClass, @NonNull SplitPairRule splitPairRule,
+            @NonNull String secondActivityId,
+            @NonNull TestValueCountConsumer<List<SplitInfo>> splitInfoConsumer) {
+        return startActivityAndVerifySplit(primaryActivity, secondActivityClass, splitPairRule,
+                secondActivityId, splitInfoConsumer, 1 /* expectedCallbackCount */);
+    }
+
     @Nullable
     public static Activity getSecondActivity(@NonNull List<SplitInfo> activeSplitStates,
             @NonNull Activity primaryActivity, @NonNull String secondaryClassId) {
diff --git a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/ExtensionUtil.java b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/ExtensionUtil.java
index f0886de..a3e682b 100644
--- a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/ExtensionUtil.java
+++ b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/ExtensionUtil.java
@@ -114,8 +114,8 @@
         if (windowLayoutComponent == null) {
             return null;
         }
-        TestFirstValueConsumer<WindowLayoutInfo> windowLayoutInfoConsumer =
-                new TestFirstValueConsumer<>();
+        TestValueCountConsumer<WindowLayoutInfo> windowLayoutInfoConsumer =
+                new TestValueCountConsumer<>();
         windowLayoutComponent.addWindowLayoutInfoListener(activity, windowLayoutInfoConsumer);
         return windowLayoutInfoConsumer.waitAndGet();
     }
diff --git a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/TestFirstValueConsumer.java b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/TestFirstValueConsumer.java
deleted file mode 100644
index 2c7a27a..0000000
--- a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/TestFirstValueConsumer.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.server.wm.jetpack.utils;
-
-import androidx.annotation.Nullable;
-
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-import java.util.function.Consumer;
-
-/**
- * Consumer that provides a simple way to wait for a value to be received within a timeout and then
- * return it.
- */
-public class TestFirstValueConsumer<T> implements Consumer<T> {
-
-    private static final long TIMEOUT_MS = 3000;
-
-    CompletableFuture<T> mFuture;
-
-    public TestFirstValueConsumer() {
-        reset();
-    }
-
-    @Override
-    public void accept(T value) {
-        mFuture.complete(value);
-    }
-
-    public void reset() {
-        mFuture = new CompletableFuture<T>();
-    }
-
-    @Nullable
-    public T waitAndGet()
-            throws ExecutionException, InterruptedException, TimeoutException {
-        return mFuture.get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
-    }
-}
diff --git a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/TestValueCountConsumer.java b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/TestValueCountConsumer.java
new file mode 100644
index 0000000..4ebeac4
--- /dev/null
+++ b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/TestValueCountConsumer.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server.wm.jetpack.utils;
+
+import androidx.annotation.Nullable;
+
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+
+/**
+ * Consumer that provides a simple way to wait for a specific count of values to be received within
+ * a timeout and then return the last value.
+ */
+public class TestValueCountConsumer<T> implements Consumer<T> {
+
+    private static final long TIMEOUT_MS = 3000;
+    private static final int DEFAULT_COUNT = 1;
+    private int mCount = DEFAULT_COUNT;
+    private LinkedBlockingQueue<T> mLinkedBlockingQueue;
+    private T mLastReportedValue;
+
+    public TestValueCountConsumer() {
+        mLinkedBlockingQueue = new LinkedBlockingQueue<>();
+    }
+
+    @Override
+    public void accept(T value) {
+        // Asynchronously offer value to queue
+        mLinkedBlockingQueue.offer(value);
+    }
+
+    public void setCount(int count) {
+        mCount = count;
+    }
+
+    @Nullable
+    public T waitAndGet() throws InterruptedException {
+        T value = null;
+        for (int i = 0; i < mCount; i++) {
+            value = mLinkedBlockingQueue.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+        }
+        mLastReportedValue = value;
+        return value;
+    }
+
+    @Nullable
+    public T getLastReportedValue() {
+        return mLastReportedValue;
+    }
+}