Android: Make androidx.window.extensions library available

This does not yet add it to chrome.

Bug: 1415351
Change-Id: Ie4075e9e0f9ffc7d89fe60950c01b45b69552f46
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5148729
Reviewed-by: Mohamed Heikal <mheikal@chromium.org>
Commit-Queue: Andrew Grieve <agrieve@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1242020}
NOKEYCHECK=True
GitOrigin-RevId: 70a1f51e438fd782bedac6fd76193270638678f4
diff --git a/BUILD.gn b/BUILD.gn
index 970675e..7b3ff66 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -25,11 +25,19 @@
       variables = [ "library_name=${invoker.library_name}" ]
     }
     java_group(target_name) {
+      forward_variables_from(invoker, [ "jar_deps" ])
       deps = [ ":$_manifest_target" ]
+      if (defined(invoker.deps)) {
+        deps += invoker.deps
+      }
 
       # Makes the .jar appear in the classpath for compile steps.
-      input_jars_paths =
-          [ "${android_sdk}/optional/${invoker.library_name}.jar" ]
+      if (defined(invoker.input_jars_paths)) {
+        input_jars_paths = invoker.input_jars_paths
+      } else {
+        input_jars_paths =
+            [ "${android_sdk}/optional/${invoker.library_name}.jar" ]
+      }
 
       # Adds the <uses-library> tag to the manifest.
       mergeable_android_manifests = [ _manifest_path ]
@@ -51,4 +59,12 @@
   android_sdk_optional_library("android_car_java") {
     library_name = "android.car"
   }
+  android_sdk_optional_library("android_window_extensions_java") {
+    library_name = "androidx.window.extensions"
+    jar_deps = [
+      "//third_party/android_sdk/window_extensions:androidx_window_extensions",
+    ]
+    deps = [ "//third_party/androidx:androidx_annotation_annotation_jvm_java" ]
+    input_jars_paths = [ "$root_build_dir/obj/third_party/android_sdk/window_extensions/androidx_window_extensions_java.javac.jar" ]
+  }
 }
diff --git a/window_extensions/BUILD.gn b/window_extensions/BUILD.gn
new file mode 100644
index 0000000..ba3c865
--- /dev/null
+++ b/window_extensions/BUILD.gn
@@ -0,0 +1,25 @@
+# Copyright 2023 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/android/rules.gni")
+
+# Contains placeholder files that define the API provided by the system library.
+# The method bodies are likely different on actual devices.
+android_library("androidx_window_extensions_java") {
+  visibility = [
+    ":*",
+  ]
+  sources = rebase_path(read_file("sources.txt", "list lines"), ".", "java")
+  deps = [
+    "//third_party/androidx:androidx_annotation_annotation_jvm_java",
+    ]
+}
+
+# Use an intermediate target that omits the "_java" suffix in order to avoid
+# the classes being linked into APKs (our build system links in targets based
+# on naming patterns).
+group("androidx_window_extensions") {
+  visibility = ["//third_party/android_sdk:*"]
+  deps = [ ":androidx_window_extensions_java" ]
+}
diff --git a/window_extensions/README.md b/window_extensions/README.md
new file mode 100644
index 0000000..9e176be
--- /dev/null
+++ b/window_extensions/README.md
@@ -0,0 +1,24 @@
+# androidx.window.extensions
+
+This library is not available as a prebuilt like other optional system
+libraries in order to encourage clients to access it via the `androidx.window`
+wrapper library. However, Chrome uses it directly in order to reduce the binary
+size overhead of the wrapper library. Googlers, see: [b/233785784].
+
+As with other optional SDK libraries, these classes are not compiled in. They
+are available at runtime through a system library loaded by the framework
+when a `<uses-library>` tag exists in an app's `AndroidManifest.xml` (which is
+added via manifest merging when depending on the GN target).
+
+Not all devices have this optional library available. You can check if it's
+available via `org.chromium.window.WindowUtil#isAvailable()`.
+
+Library documentation: https://source.android.com/docs/core/display/windowmanager-extensions
+
+Library source code: https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:window/extensions/
+
+[b/233785784]: http://b/233785784
+
+## Updating the Sources
+
+Run `update_sources.sh`
diff --git a/window_extensions/java/window/extensions/core/core/src/main/java/androidx/window/extensions/core/util/function/Consumer.java b/window_extensions/java/window/extensions/core/core/src/main/java/androidx/window/extensions/core/util/function/Consumer.java
new file mode 100644
index 0000000..8fee672
--- /dev/null
+++ b/window_extensions/java/window/extensions/core/core/src/main/java/androidx/window/extensions/core/util/function/Consumer.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2023 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 androidx.window.extensions.core.util.function;
+
+/**
+ * Represents a function that accepts an argument and produces no result.
+ * It is used internally to avoid using Java 8 functional interface that leads to desugaring and
+ * Proguard shrinking.
+ *
+ * @param <T>: the type of the input of the function
+ *
+ * @see java.util.function.Consumer
+ */
+@FunctionalInterface
+public interface Consumer<T> {
+    /**
+     * Performs the operation on the given argument
+     *
+     * @param t the input argument
+     */
+    void accept(T t);
+}
diff --git a/window_extensions/java/window/extensions/core/core/src/main/java/androidx/window/extensions/core/util/function/Function.java b/window_extensions/java/window/extensions/core/core/src/main/java/androidx/window/extensions/core/util/function/Function.java
new file mode 100644
index 0000000..4bc7bcb
--- /dev/null
+++ b/window_extensions/java/window/extensions/core/core/src/main/java/androidx/window/extensions/core/util/function/Function.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2023 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 androidx.window.extensions.core.util.function;
+
+/**
+ * Represents a function that accepts an argument and produces a result.
+ * It is used internally to avoid using Java 8 functional interface that leads to desugaring and
+ * Proguard shrinking.
+ *
+ * @param <T>: the type of the input of the function
+ * @param <R>: the type of the output of the function
+ *
+ * @see java.util.function.Function
+ */
+@FunctionalInterface
+public interface Function<T, R> {
+    /**
+     * Applies this function to the given argument.
+     *
+     * @param t the function argument
+     * @return the function result
+     */
+    R apply(T t);
+}
diff --git a/window_extensions/java/window/extensions/core/core/src/main/java/androidx/window/extensions/core/util/function/Predicate.java b/window_extensions/java/window/extensions/core/core/src/main/java/androidx/window/extensions/core/util/function/Predicate.java
new file mode 100644
index 0000000..4942523
--- /dev/null
+++ b/window_extensions/java/window/extensions/core/core/src/main/java/androidx/window/extensions/core/util/function/Predicate.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2023 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 androidx.window.extensions.core.util.function;
+
+/**
+ * Represents a predicate boolean-valued function of one argument.
+ * It is used internally to avoid using Java 8 functional interface that leads to desugaring and
+ * Proguard shrinking.
+ *
+ * @param <T> the type of the input to the predicate
+ *
+ * @see java.util.function.Predicate
+ */
+@FunctionalInterface
+public interface Predicate<T> {
+    /**
+     * Tests the predicate against a given argument.
+     *
+     * @param t the input of the predicate
+     * @return {@code true} if the input matches the {@code Predicate}, otherwise, {@code false}
+     */
+    boolean test(T t);
+}
diff --git a/window_extensions/java/window/extensions/extensions/src/main/java/androidx/window/extensions/WindowExtensions.java b/window_extensions/java/window/extensions/extensions/src/main/java/androidx/window/extensions/WindowExtensions.java
new file mode 100644
index 0000000..74163e6
--- /dev/null
+++ b/window_extensions/java/window/extensions/extensions/src/main/java/androidx/window/extensions/WindowExtensions.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright 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 androidx.window.extensions;
+
+import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.app.Activity;
+import android.app.ActivityOptions;
+import android.content.Context;
+import android.os.IBinder;
+
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+import androidx.window.extensions.area.WindowAreaComponent;
+import androidx.window.extensions.core.util.function.Consumer;
+import androidx.window.extensions.embedding.ActivityEmbeddingComponent;
+import androidx.window.extensions.embedding.ActivityStack;
+import androidx.window.extensions.embedding.SplitAttributes;
+import androidx.window.extensions.embedding.SplitInfo;
+import androidx.window.extensions.layout.WindowLayoutComponent;
+
+import java.util.Set;
+
+/**
+ * A class to provide instances of different WindowManager Jetpack extension components. An OEM must
+ * implement all the availability methods to state which WindowManager Jetpack extension
+ * can be used. If a component is not available then the check must return {@code false}. Trying to
+ * get a component that is not available will throw an {@link UnsupportedOperationException}.
+ * All components must support the API level returned in
+ * {@link WindowExtensions#getVendorApiLevel()}.
+ */
+public interface WindowExtensions {
+    // TODO(b/241323716) Removed after we have annotation to check API level
+    /**
+     * An invalid {@link #getVendorApiLevel vendor API level}
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    int INVALID_VENDOR_API_LEVEL = -1;
+
+    // TODO(b/241323716) Removed after we have annotation to check API level
+    /**
+     * A vendor API level constant. It helps to unify the format of documenting {@code @since}
+     * block.
+     * <p>
+     * The added APIs for Vendor API level 1 are:
+     * <ul>
+     *     <li>{@link androidx.window.extensions.embedding.ActivityRule} APIs</li>
+     *     <li>{@link androidx.window.extensions.embedding.SplitPairRule} APIs</li>
+     *     <li>{@link androidx.window.extensions.embedding.SplitPlaceholderRule} APIs</li>
+     *     <li>{@link androidx.window.extensions.embedding.SplitInfo} APIs</li>
+     *     <li>{@link androidx.window.extensions.layout.DisplayFeature} APIs</li>
+     *     <li>{@link androidx.window.extensions.layout.FoldingFeature} APIs</li>
+     *     <li>{@link androidx.window.extensions.layout.WindowLayoutInfo} APIs</li>
+     *     <li>{@link androidx.window.extensions.layout.WindowLayoutComponent} APIs</li>
+     * </ul>
+     * </p>
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    int VENDOR_API_LEVEL_1 = 1;
+
+    // TODO(b/241323716) Removed after we have annotation to check API level
+    /**
+     * A vendor API level constant. It helps to unify the format of documenting {@code @since}
+     * block.
+     * The added APIs for Vendor API level 2 are:
+     * <ul>
+     *     <li>{@link WindowAreaComponent#addRearDisplayStatusListener(Consumer)}</li>
+     *     <li>{@link WindowAreaComponent#startRearDisplaySession(Activity, Consumer)}</li>
+     *     <li>{@link androidx.window.extensions.embedding.SplitPlaceholderRule.Builder#setFinishPrimaryWithPlaceholder(int)}</li>
+     *     <li>{@link androidx.window.extensions.embedding.SplitAttributes}</li>
+     *     <li>{@link ActivityEmbeddingComponent#setSplitAttributesCalculator(
+     *      androidx.window.extensions.core.util.function.Function)}</li>
+     *     <li>{@link WindowLayoutComponent#addWindowLayoutInfoListener(Context, Consumer)}</li>
+     * </ul>
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    int VENDOR_API_LEVEL_2 = 2;
+
+    // TODO(b/241323716) Removed after we have annotation to check API level
+    /**
+     * A vendor API level constant. It helps to unify the format of documenting {@code @since}
+     * block.
+     * <p>
+     * The added APIs for Vendor API level 3 are:
+     * <ul>
+     *     <li>{@link ActivityStack#getToken()}</li>
+     *     <li>{@link SplitInfo#getToken()}</li>
+     *     <li>{@link ActivityEmbeddingComponent#setLaunchingActivityStack(ActivityOptions,
+     *     IBinder)}</li>
+     *     <li>{@link ActivityEmbeddingComponent#invalidateTopVisibleSplitAttributes()}</li>
+     *     <li>{@link ActivityEmbeddingComponent#updateSplitAttributes(IBinder, SplitAttributes)}
+     *     </li>
+     *     <li>{@link ActivityEmbeddingComponent#finishActivityStacks(Set)}</li>
+     *     <li>{@link WindowAreaComponent#addRearDisplayPresentationStatusListener(Consumer)}</li>
+     *     <li>{@link WindowAreaComponent#startRearDisplayPresentationSession(Activity, Consumer)}
+     *     </li>
+     *     <li>{@link WindowAreaComponent#getRearDisplayMetrics()}</li>
+     *     <li>{@link WindowAreaComponent#getRearDisplayPresentation()}</li>
+     * </ul>
+     * </p>
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    int VENDOR_API_LEVEL_3 = 3;
+
+    /**
+     * Returns the API level of the vendor library on the device. If the returned version is not
+     * supported by the WindowManager library, then some functions may not be available or replaced
+     * with stub implementations.
+     *
+     * The expected use case is for the WindowManager library to determine which APIs are
+     * available and wrap the API so that app developers do not need to deal with the complexity.
+     * @return the API level supported by the library.
+     */
+    default int getVendorApiLevel() {
+        throw new RuntimeException("Not implemented. Must override in a subclass.");
+    }
+
+    /**
+     * Returns the OEM implementation of {@link WindowLayoutComponent} if it is supported on the
+     * device, {@code null} otherwise. The implementation must match the API level reported in
+     * {@link WindowExtensions}.
+     * @return the OEM implementation of {@link WindowLayoutComponent}
+     */
+    @Nullable
+    WindowLayoutComponent getWindowLayoutComponent();
+
+    /**
+     * Returns the OEM implementation of {@link ActivityEmbeddingComponent} if it is supported on
+     * the device, {@code null} otherwise. The implementation must match the API level reported in
+     * {@link WindowExtensions}.
+     * @return the OEM implementation of {@link ActivityEmbeddingComponent}
+     */
+    @Nullable
+    default ActivityEmbeddingComponent getActivityEmbeddingComponent() {
+        return null;
+    }
+
+    /**
+     * Returns the OEM implementation of {@link WindowAreaComponent} if it is supported on
+     * the device, {@code null} otherwise. The implementation must match the API level reported in
+     * {@link WindowExtensions}.
+     * @return the OEM implementation of {@link WindowAreaComponent}
+     */
+    @Nullable
+    default WindowAreaComponent getWindowAreaComponent() {
+        return null;
+    }
+}
diff --git a/window_extensions/java/window/extensions/extensions/src/main/java/androidx/window/extensions/WindowExtensionsProvider.java b/window_extensions/java/window/extensions/extensions/src/main/java/androidx/window/extensions/WindowExtensionsProvider.java
new file mode 100644
index 0000000..1f3aefd
--- /dev/null
+++ b/window_extensions/java/window/extensions/extensions/src/main/java/androidx/window/extensions/WindowExtensionsProvider.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 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 androidx.window.extensions;
+
+import androidx.annotation.NonNull;
+
+/**
+ * Provides the OEM implementation of {@link WindowExtensions}.
+ */
+public class WindowExtensionsProvider {
+
+    private WindowExtensionsProvider() {}
+
+    /**
+     * Returns the OEM implementation of {@link WindowExtensions}. This method must be
+     * implemented by the OEM also.
+     * @return the OEM implementation of {@link WindowExtensions}
+     */
+    @NonNull
+    public static WindowExtensions getWindowExtensions() {
+        throw new UnsupportedOperationException("Stub, replace with implementation");
+    }
+}
diff --git a/window_extensions/java/window/extensions/extensions/src/main/java/androidx/window/extensions/area/ExtensionWindowAreaPresentation.java b/window_extensions/java/window/extensions/extensions/src/main/java/androidx/window/extensions/area/ExtensionWindowAreaPresentation.java
new file mode 100644
index 0000000..0ce24b8
--- /dev/null
+++ b/window_extensions/java/window/extensions/extensions/src/main/java/androidx/window/extensions/area/ExtensionWindowAreaPresentation.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2023 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 androidx.window.extensions.area;
+
+import android.content.Context;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+
+/**
+ * An interface representing a container in an extension window area in which app content can be
+ * shown.
+ *
+ * Since {@link androidx.window.extensions.WindowExtensions#VENDOR_API_LEVEL_3}
+ * @see WindowAreaComponent#getRearDisplayPresentation()
+ */
+public interface ExtensionWindowAreaPresentation {
+
+    /**
+     * Returns the {@link Context} for the window that is being used
+     * to display the additional content provided from the application.
+     */
+    @NonNull
+    Context getPresentationContext();
+
+    /**
+     * Sets the {@link View} that the application wants to display in the extension window area.
+     */
+    void setPresentationView(@NonNull View view);
+}
diff --git a/window_extensions/java/window/extensions/extensions/src/main/java/androidx/window/extensions/area/ExtensionWindowAreaStatus.java b/window_extensions/java/window/extensions/extensions/src/main/java/androidx/window/extensions/area/ExtensionWindowAreaStatus.java
new file mode 100644
index 0000000..0dcd47f
--- /dev/null
+++ b/window_extensions/java/window/extensions/extensions/src/main/java/androidx/window/extensions/area/ExtensionWindowAreaStatus.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2023 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 androidx.window.extensions.area;
+
+import android.util.DisplayMetrics;
+
+import androidx.annotation.NonNull;
+
+/**
+ * Interface to provide information around the current status of a window area feature.
+ *
+ * Since {@link androidx.window.extensions.WindowExtensions#VENDOR_API_LEVEL_3}
+ * @see WindowAreaComponent#addRearDisplayPresentationStatusListener
+ */
+public interface ExtensionWindowAreaStatus {
+
+    /**
+     * Returns the {@link androidx.window.extensions.area.WindowAreaComponent.WindowAreaStatus}
+     * value that relates to the current status of a feature.
+     */
+    @WindowAreaComponent.WindowAreaStatus
+    int getWindowAreaStatus();
+
+    /**
+     * Returns the {@link DisplayMetrics} that corresponds to the window area that a feature
+     * interacts with. This is converted to size class information provided to developers.
+     */
+    @NonNull
+    DisplayMetrics getWindowAreaDisplayMetrics();
+}
diff --git a/window_extensions/java/window/extensions/extensions/src/main/java/androidx/window/extensions/area/WindowAreaComponent.java b/window_extensions/java/window/extensions/extensions/src/main/java/androidx/window/extensions/area/WindowAreaComponent.java
new file mode 100644
index 0000000..e5705ac
--- /dev/null
+++ b/window_extensions/java/window/extensions/extensions/src/main/java/androidx/window/extensions/area/WindowAreaComponent.java
@@ -0,0 +1,277 @@
+/*
+ * Copyright 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 androidx.window.extensions.area;
+
+import android.app.Activity;
+import android.util.DisplayMetrics;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+import androidx.window.extensions.WindowExtensions;
+import androidx.window.extensions.core.util.function.Consumer;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * The interface definition that will be used by the WindowManager library to get custom
+ * OEM-provided behavior around moving windows between displays or display areas on a device.
+ *
+ * Currently the only behavior supported is RearDisplay Mode, where the window
+ * is moved to the display that faces the same direction as the rear camera.
+ *
+ * <p>This interface should be implemented by OEM and deployed to the target devices.
+ * @see WindowExtensions#getWindowLayoutComponent()
+ */
+public interface WindowAreaComponent {
+
+    /**
+     * WindowArea status constant to signify that the feature is
+     * unsupported on this device. Could be due to the device not supporting that
+     * specific feature.
+     *
+     * Since {@link WindowExtensions#VENDOR_API_LEVEL_2}
+     */
+    int STATUS_UNSUPPORTED = 0;
+
+    /**
+     * WindowArea status constant to signify that the feature is
+     * currently unavailable but is supported on this device. This value could signify
+     * that the current device state does not support the specific feature or another
+     * process is currently enabled in that feature.
+     *
+     * Since {@link WindowExtensions#VENDOR_API_LEVEL_2}
+     */
+    int STATUS_UNAVAILABLE = 1;
+
+    /**
+     * WindowArea status constant to signify that the feature is
+     * available to be entered or enabled.
+     *
+     * Since {@link WindowExtensions#VENDOR_API_LEVEL_2}
+     */
+    int STATUS_AVAILABLE = 2;
+
+    /**
+     * WindowArea status constant to signify that the feature is
+     * already enabled.
+     *
+     * Since {@link WindowExtensions#VENDOR_API_LEVEL_3}
+     */
+    int STATUS_ACTIVE = 3;
+
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    @Retention(RetentionPolicy.SOURCE)
+    @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
+    @IntDef({
+            STATUS_UNSUPPORTED,
+            STATUS_UNAVAILABLE,
+            STATUS_AVAILABLE,
+            STATUS_ACTIVE
+    })
+    @interface WindowAreaStatus {}
+
+    /**
+     * Session state constant to represent there being no active session
+     * currently in progress. Used by the library to call the correct callbacks if
+     * a session is ended.
+     */
+    int SESSION_STATE_INACTIVE = 0;
+
+    /**
+     * Session state constant to represent that there is currently an active session. The library
+     * uses this state to know when a session is created and active. Note that this state is
+     * different from SESSION_STATE_CONTENT_VISIBLE, because the presentation content in this state
+     * is not visible.
+     */
+    int SESSION_STATE_ACTIVE = 1;
+
+    /**
+     * Session state constant to represent that there is an
+     * active presentation session currently in progress, and the content provided by the
+     * application is visible.
+     */
+    int SESSION_STATE_CONTENT_VISIBLE = 2;
+
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    @Retention(RetentionPolicy.SOURCE)
+    @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
+    @IntDef({
+            SESSION_STATE_ACTIVE,
+            SESSION_STATE_INACTIVE,
+            SESSION_STATE_CONTENT_VISIBLE
+    })
+    @interface WindowAreaSessionState {}
+
+    /**
+     * Adds a listener interested in receiving updates on the RearDisplayStatus
+     * of the device. Because this is being called from the OEM provided
+     * extensions, the library will post the result of the listener on the executor
+     * provided by the developer.
+     *
+     * The listener provided will receive values that
+     * correspond to the [WindowAreaStatus] value that aligns with the current status
+     * of the rear display.
+     * @param consumer interested in receiving updates to WindowAreaStatus.
+     * Since {@link WindowExtensions#VENDOR_API_LEVEL_2}
+     */
+    void addRearDisplayStatusListener(@NonNull Consumer<Integer> consumer);
+
+    /**
+     * Removes a listener no longer interested in receiving updates.
+     * @param consumer no longer interested in receiving updates to WindowAreaStatus
+     * Since {@link WindowExtensions#VENDOR_API_LEVEL_2}
+     */
+    void removeRearDisplayStatusListener(@NonNull Consumer<Integer> consumer);
+
+    /**
+     * Creates and starts a rear display session and sends state updates to the
+     * consumer provided. This consumer will receive a constant represented by
+     * [WindowAreaSessionState] to represent the state of the current rear display
+     * session. We will translate to a more friendly interface in the library.
+     *
+     * Because this is being called from the OEM provided extensions, the library
+     * will post the result of the listener on the executor provided by the developer.
+     *
+     * @param activity to allow that the OEM implementation will use as a base
+     * context and to identify the source display area of the request.
+     * The reference to the activity instance must not be stored in the OEM
+     * implementation to prevent memory leaks.
+     * @param consumer to provide updates to the client on the status of the session
+     * @throws UnsupportedOperationException if this method is called when RearDisplay
+     * mode is not available. This could be to an incompatible device state or when
+     * another process is currently in this mode.
+     * Since {@link WindowExtensions#VENDOR_API_LEVEL_2}
+     */
+    @SuppressWarnings("ExecutorRegistration") // Jetpack will post it on the app-provided executor.
+    void startRearDisplaySession(@NonNull Activity activity,
+            @NonNull Consumer<@WindowAreaSessionState Integer> consumer);
+
+    /**
+     * Ends a RearDisplaySession and sends [STATE_INACTIVE] to the consumer
+     * provided in the {@code startRearDisplaySession} method. This method is only
+     * called through the {@code RearDisplaySession} provided to the developer.
+     * Since {@link WindowExtensions#VENDOR_API_LEVEL_2}
+     */
+    void endRearDisplaySession();
+
+    /**
+     * Adds a listener interested in receiving updates on the rear display presentation status
+     * of the device. Because this is being called from the OEM provided
+     * extensions, the library will post the result of the listener on the executor
+     * provided by the developer.
+     *
+     * The listener provided will receive {@link ExtensionWindowAreaStatus} values that
+     * correspond to the current status of the feature.
+     *
+     * @param consumer interested in receiving updates to {@link ExtensionWindowAreaStatus}.
+     * Since {@link WindowExtensions#VENDOR_API_LEVEL_3}
+     */
+    default void addRearDisplayPresentationStatusListener(
+            @NonNull Consumer<ExtensionWindowAreaStatus> consumer) {
+        throw new UnsupportedOperationException("This method must not be called unless there is a"
+                + " corresponding override implementation on the device.");
+    }
+
+    /**
+     * Removes a listener no longer interested in receiving updates.
+     *
+     * @param consumer no longer interested in receiving updates to WindowAreaStatus
+     * Since {@link WindowExtensions#VENDOR_API_LEVEL_3}
+     */
+    default void removeRearDisplayPresentationStatusListener(
+            @NonNull Consumer<ExtensionWindowAreaStatus> consumer) {
+        throw new UnsupportedOperationException("This method must not be called unless there is a"
+                + " corresponding override implementation on the device.");
+    }
+
+    /**
+     * Creates and starts a rear display presentation session and sends state updates to the
+     * consumer provided. This consumer will receive a constant represented by
+     * {@link WindowAreaSessionState} to represent the state of the current rear display
+     * session. We will translate to a more friendly interface in the library.
+     *
+     * Because this is being called from the OEM provided extensions, the library
+     * will post the result of the listener on the executor provided by the developer.
+     *
+     * Rear display presentation mode refers to a feature where an {@link Activity} can present
+     * additional content on a device with a second display that is facing the same direction
+     * as the rear camera (i.e. the cover display on a fold-in style device). The calling
+     * {@link Activity} stays on the user-facing display.
+     *
+     * @param activity that the OEM implementation will use as a base
+     * context and to identify the source display area of the request.
+     * The reference to the activity instance must not be stored in the OEM
+     * implementation to prevent memory leaks.
+     * @param consumer to provide updates to the client on the status of the session
+     * @throws UnsupportedOperationException if this method is called when rear display presentation
+     * mode is not available. This could be to an incompatible device state or when
+     * another process is currently in this mode.
+     * Since {@link WindowExtensions#VENDOR_API_LEVEL_3}
+     */
+    default void startRearDisplayPresentationSession(@NonNull Activity activity,
+            @NonNull Consumer<@WindowAreaSessionState Integer> consumer) {
+        throw new UnsupportedOperationException("This method must not be called unless there is a"
+                + " corresponding override implementation on the device.");
+    }
+
+    /**
+     * Ends the current rear display presentation session and provides updates to the
+     * callback provided. When this is ended, the presented content from the calling
+     * {@link Activity} will also be removed from the rear facing display.
+     * Because this is being called from the OEM provided extensions, the result of the listener
+     * will be posted on the executor provided by the developer at the initial call site.
+     *
+     * Since {@link WindowExtensions#VENDOR_API_LEVEL_3}
+     */
+    default void endRearDisplayPresentationSession() {
+        throw new UnsupportedOperationException("This method must not be called unless there is a"
+                + " corresponding override implementation on the device.");
+    }
+
+    /**
+     * Returns the {@link ExtensionWindowAreaPresentation} connected to the active
+     * rear display presentation session. If there is no session currently active, then it will
+     * return null.
+     *
+     * Since {@link WindowExtensions#VENDOR_API_LEVEL_3}
+     */
+    @Nullable
+    default ExtensionWindowAreaPresentation getRearDisplayPresentation() {
+        throw new UnsupportedOperationException("This method must not be called unless there is a"
+                + " corresponding override implementation on the device.");
+    }
+
+    /**
+     * Returns the {@link android.util.DisplayMetrics} associated with the rear facing display. If
+     * there is no rear facing display available on the device, returns an empty
+     * {@link android.util.DisplayMetrics} object.
+     *
+     * Since {@link WindowExtensions#VENDOR_API_LEVEL_3}
+     */
+    // TODO(b/273807238): Investigate how we can provide a listener to get runtime changes in
+    //  rear display metrics to better support other form-factors in the future.
+    @NonNull
+    default DisplayMetrics getRearDisplayMetrics() {
+        throw new UnsupportedOperationException("This method must not be called unless there is a"
+                + " corresponding override implementation on the device.");
+    }
+}
diff --git a/window_extensions/java/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/ActivityEmbeddingComponent.java b/window_extensions/java/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/ActivityEmbeddingComponent.java
new file mode 100644
index 0000000..121520c
--- /dev/null
+++ b/window_extensions/java/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/ActivityEmbeddingComponent.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright 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 androidx.window.extensions.embedding;
+
+import android.app.Activity;
+import android.app.ActivityOptions;
+import android.os.IBinder;
+import android.view.WindowMetrics;
+
+import androidx.annotation.NonNull;
+import androidx.window.extensions.WindowExtensions;
+import androidx.window.extensions.core.util.function.Consumer;
+import androidx.window.extensions.core.util.function.Function;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Extension component definition that is used by the WindowManager library to trigger custom
+ * OEM-provided methods for organizing activities that isn't covered by platform APIs.
+ *
+ * <p>This interface should be implemented by OEM and deployed to the target devices.
+ * @see androidx.window.extensions.WindowExtensions
+ */
+public interface ActivityEmbeddingComponent {
+
+    /**
+     * Updates the rules of embedding activities that are started in the client process.
+     * Since {@link WindowExtensions#VENDOR_API_LEVEL_1}
+     */
+    void setEmbeddingRules(@NonNull Set<EmbeddingRule> splitRules);
+
+    /**
+     * @deprecated Use {@link #setSplitInfoCallback(Consumer)} starting with
+     * {@link WindowExtensions#VENDOR_API_LEVEL_2}. Only used if
+     * {@link #setSplitInfoCallback(Consumer)} can't be called on
+     * {@link WindowExtensions#VENDOR_API_LEVEL_1}.
+     * Since {@link WindowExtensions#VENDOR_API_LEVEL_1}
+     */
+    @Deprecated
+    @SuppressWarnings("ExecutorRegistration") // Jetpack will post it on the app-provided executor.
+    void setSplitInfoCallback(@NonNull java.util.function.Consumer<List<SplitInfo>> consumer);
+
+    /**
+     * Sets the callback that notifies WM Jetpack about changes in split states from the Extensions
+     * Sidecar implementation. The listener should be registered for the lifetime of the process.
+     * There are no threading guarantees where the events are dispatched from. All messages are
+     * re-posted to the executors provided by developers.
+     *
+     * @param consumer the callback to notify {@link SplitInfo} list changes
+     * Since {@link WindowExtensions#VENDOR_API_LEVEL_2}
+     */
+    @SuppressWarnings("ExecutorRegistration") // Jetpack will post it on the app-provided executor.
+    default void setSplitInfoCallback(@NonNull Consumer<List<SplitInfo>> consumer) {
+        throw new UnsupportedOperationException("This method must not be called unless there is a"
+                + " corresponding override implementation on the device.");
+    }
+
+    /**
+     * Clears the callback that was set in
+     * {@link ActivityEmbeddingComponent#setSplitInfoCallback(Consumer)}.
+     * Added in {@link WindowExtensions#getVendorApiLevel()} 2, calling an earlier version will
+     * throw {@link java.lang.NoSuchMethodError}.
+     * Since {@link WindowExtensions#VENDOR_API_LEVEL_2}
+     */
+    void clearSplitInfoCallback();
+
+    /**
+     * Checks if an activity's' presentation is customized by its or any other process and only
+     * occupies a portion of Task bounds.
+     * Since {@link WindowExtensions#VENDOR_API_LEVEL_1}
+     */
+    boolean isActivityEmbedded(@NonNull Activity activity);
+
+    /**
+     * Sets a function to compute the {@link SplitAttributes} for the {@link SplitRule} and current
+     * window state provided in {@link SplitAttributesCalculatorParams}.
+     * <p>
+     * This method can be used to dynamically configure the split layout properties when new
+     * activities are launched or window properties change.
+     * <p>
+     * If the {@link SplitAttributes} calculator function is not set or is cleared by
+     * {@link #clearSplitAttributesCalculator()}, apps will update its split layout with
+     * registered {@link SplitRule} configurations:
+     * <ul>
+     *     <li>Split with {@link SplitRule#getDefaultSplitAttributes()} if parent task
+     *     container size constraints defined by
+     *     {@link SplitRule#checkParentMetrics(WindowMetrics)} are satisfied</li>
+     *     <li>Occupy the whole parent task bounds if the constraints are not satisfied. </li>
+     * </ul>
+     * <p>
+     * If the function is set, {@link SplitRule#getDefaultSplitAttributes()} and
+     * {@link SplitRule#checkParentMetrics(WindowMetrics)} will be passed to
+     * {@link SplitAttributesCalculatorParams} as
+     * {@link SplitAttributesCalculatorParams#getDefaultSplitAttributes()} and
+     * {@link SplitAttributesCalculatorParams#areDefaultConstraintsSatisfied()} instead, and the
+     * function will be invoked for every device and window state change regardless of the size
+     * constraints. Users can determine to follow the {@link SplitRule} behavior or customize
+     * the {@link SplitAttributes} with the {@link SplitAttributes} calculator function.
+     *
+     * @param calculator the callback to set. It will replace the previously set callback if it
+     *                  exists.
+     * Since {@link WindowExtensions#VENDOR_API_LEVEL_2}
+     */
+    void setSplitAttributesCalculator(
+            @NonNull Function<SplitAttributesCalculatorParams, SplitAttributes> calculator);
+
+    /**
+     * Clears the previously callback set in {@link #setSplitAttributesCalculator(Function)}.
+     *
+     * @see #setSplitAttributesCalculator(Function)
+     * Since {@link WindowExtensions#VENDOR_API_LEVEL_2}
+     */
+    void clearSplitAttributesCalculator();
+
+    /**
+     * Sets the launching {@link ActivityStack} to the given {@link ActivityOptions}.
+     *
+     * @param options The {@link ActivityOptions} to be updated.
+     * @param token The {@link ActivityStack#getToken()} to represent the {@link ActivityStack}
+     * Since {@link WindowExtensions#VENDOR_API_LEVEL_3}
+     */
+    @NonNull
+    default ActivityOptions setLaunchingActivityStack(@NonNull ActivityOptions options,
+            @NonNull IBinder token) {
+        throw new UnsupportedOperationException("This method must not be called unless there is a"
+                + " corresponding override implementation on the device.");
+    }
+
+    /**
+     * Finishes a set of {@link ActivityStack}s. When an {@link ActivityStack} that was in an active
+     * split is finished, the other {@link ActivityStack} in the same {@link SplitInfo} can be
+     * expanded to fill the parent task container.
+     *
+     * @param activityStackTokens The set of tokens of {@link ActivityStack}-s that is going to be
+     *                            finished.
+     * Since {@link WindowExtensions#VENDOR_API_LEVEL_3}
+     */
+    default void finishActivityStacks(@NonNull Set<IBinder> activityStackTokens) {
+        throw new UnsupportedOperationException("This method must not be called unless there is a"
+                + " corresponding override implementation on the device.");
+    }
+
+    /**
+     * Triggers an update of the split attributes for the top split if there is one visible by
+     * making extensions invoke the split attributes calculator callback. This method can be used
+     * when a change to the split presentation originates from the application state change rather
+     * than driven by parent window changes or new activity starts. The call will be ignored if
+     * there is no visible split.
+     * @see #setSplitAttributesCalculator(Function)
+     * Since {@link WindowExtensions#VENDOR_API_LEVEL_3}
+     */
+    default void invalidateTopVisibleSplitAttributes() {
+        throw new UnsupportedOperationException("This method must not be called unless there is a"
+                + " corresponding override implementation on the device.");
+    }
+
+    /**
+     * Updates the {@link SplitAttributes} of a split pair. This is an alternative to using
+     * a split attributes calculator callback, applicable when apps only need to update the
+     * splits in a few cases but rely on the default split attributes otherwise.
+     * @param splitInfoToken The identifier of the split pair to update.
+     * @param splitAttributes The {@link SplitAttributes} to apply to the split pair.
+     * Since {@link WindowExtensions#VENDOR_API_LEVEL_3}
+     */
+    default void updateSplitAttributes(@NonNull IBinder splitInfoToken,
+            @NonNull SplitAttributes splitAttributes) {
+        throw new UnsupportedOperationException("This method must not be called unless there is a"
+                + " corresponding override implementation on the device.");
+    }
+}
diff --git a/window_extensions/java/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/ActivityRule.java b/window_extensions/java/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/ActivityRule.java
new file mode 100644
index 0000000..c741647
--- /dev/null
+++ b/window_extensions/java/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/ActivityRule.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright 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 androidx.window.extensions.embedding;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Build;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+import androidx.window.extensions.WindowExtensions;
+import androidx.window.extensions.core.util.function.Predicate;
+
+import java.util.Objects;
+
+/**
+ * Split configuration rule for individual activities.
+ */
+public class ActivityRule extends EmbeddingRule {
+    @NonNull
+    private final Predicate<Activity> mActivityPredicate;
+    @NonNull
+    private final Predicate<Intent> mIntentPredicate;
+    private final boolean mShouldAlwaysExpand;
+
+    ActivityRule(@NonNull Predicate<Activity> activityPredicate,
+            @NonNull Predicate<Intent> intentPredicate, boolean shouldAlwaysExpand,
+            @Nullable String tag) {
+        super(tag);
+        mActivityPredicate = activityPredicate;
+        mIntentPredicate = intentPredicate;
+        mShouldAlwaysExpand = shouldAlwaysExpand;
+    }
+
+    /**
+     * Checks if the rule is applicable to the provided activity.
+     */
+    @SuppressLint("ClassVerificationFailure") // Only called by Extensions implementation on device.
+    @RequiresApi(api = Build.VERSION_CODES.N)
+    public boolean matchesActivity(@NonNull Activity activity) {
+        return mActivityPredicate.test(activity);
+    }
+
+    /**
+     * Checks if the rule is applicable to the provided activity intent.
+     */
+    @SuppressLint("ClassVerificationFailure") // Only called by Extensions implementation on device.
+    @RequiresApi(api = Build.VERSION_CODES.N)
+    public boolean matchesIntent(@NonNull Intent intent) {
+        return mIntentPredicate.test(intent);
+    }
+
+    /**
+     * Indicates whether the activity or activities that are covered by this rule should always be
+     * launched in an expanded state and avoid the splits.
+     */
+    public boolean shouldAlwaysExpand() {
+        return mShouldAlwaysExpand;
+    }
+
+    /**
+     * Builder for {@link ActivityRule}.
+     */
+    public static final class Builder {
+        @NonNull
+        private final Predicate<Activity> mActivityPredicate;
+        @NonNull
+        private final Predicate<Intent> mIntentPredicate;
+        private boolean mAlwaysExpand;
+        @Nullable
+        private String mTag;
+
+        /**
+         * @deprecated Use {@link #Builder(Predicate, Predicate)} starting with
+         * {@link WindowExtensions#VENDOR_API_LEVEL_2}. Only used if
+         * {@link #Builder(Predicate, Predicate)} can't be called on
+         * {@link WindowExtensions#VENDOR_API_LEVEL_1}.
+         */
+        @Deprecated
+        @RequiresApi(Build.VERSION_CODES.N)
+        public Builder(@NonNull java.util.function.Predicate<Activity> activityPredicate,
+                @NonNull java.util.function.Predicate<Intent> intentPredicate) {
+            mActivityPredicate = activityPredicate::test;
+            mIntentPredicate = intentPredicate::test;
+        }
+
+        /**
+         * The {@link ActivityRule} Builder constructor
+         *
+         * @param activityPredicate the {@link Predicate} to verify if a given {@link Activity}
+         *                         matches the rule
+         * @param intentPredicate the {@link Predicate} to verify if a given {@link Intent}
+         *                         matches the rule
+         * Since {@link WindowExtensions#VENDOR_API_LEVEL_2}
+         */
+        public Builder(@NonNull Predicate<Activity> activityPredicate,
+                @NonNull Predicate<Intent> intentPredicate) {
+            mActivityPredicate = activityPredicate;
+            mIntentPredicate = intentPredicate;
+        }
+
+        /** @see ActivityRule#shouldAlwaysExpand() */
+        @NonNull
+        public Builder setShouldAlwaysExpand(boolean alwaysExpand) {
+            mAlwaysExpand = alwaysExpand;
+            return this;
+        }
+
+        /**
+         * @see ActivityRule#getTag()
+         * Since {@link androidx.window.extensions.WindowExtensions#VENDOR_API_LEVEL_2}
+         */
+        @NonNull
+        public Builder setTag(@NonNull String tag) {
+            mTag = Objects.requireNonNull(tag);
+            return this;
+        }
+
+        /** Builds a new instance of {@link ActivityRule}. */
+        @NonNull
+        public ActivityRule build() {
+            return new ActivityRule(mActivityPredicate, mIntentPredicate, mAlwaysExpand, mTag);
+        }
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof ActivityRule)) return false;
+        ActivityRule that = (ActivityRule) o;
+        return super.equals(o)
+                && mShouldAlwaysExpand == that.mShouldAlwaysExpand
+                && mActivityPredicate.equals(that.mActivityPredicate)
+                && mIntentPredicate.equals(that.mIntentPredicate);
+    }
+
+    @Override
+    public int hashCode() {
+        int result = super.hashCode();
+        result = 31 * result + mActivityPredicate.hashCode();
+        result = 31 * result + mIntentPredicate.hashCode();
+        result = 31 * result + (mShouldAlwaysExpand ? 1 : 0);
+        return result;
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        return "ActivityRule{mTag=" + getTag()
+                + "mShouldAlwaysExpand=" + mShouldAlwaysExpand + '}';
+    }
+}
diff --git a/window_extensions/java/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/ActivityStack.java b/window_extensions/java/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/ActivityStack.java
new file mode 100644
index 0000000..e568666
--- /dev/null
+++ b/window_extensions/java/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/ActivityStack.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright 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 androidx.window.extensions.embedding;
+
+import android.app.Activity;
+import android.os.Binder;
+import android.os.IBinder;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+import androidx.window.extensions.WindowExtensions;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Description of a group of activities stacked on top of each other and shown as a single
+ * container, all within the same task.
+ */
+public class ActivityStack {
+
+    /** Only used for compatibility with the deprecated constructor. */
+    private static final IBinder INVALID_ACTIVITY_STACK_TOKEN = new Binder();
+
+    @NonNull
+    private final List<Activity> mActivities;
+
+    private final boolean mIsEmpty;
+
+    @NonNull
+    private final IBinder mToken;
+
+    /**
+     * The {@code ActivityStack} constructor
+     *
+     * @param activities {@link Activity Activities} in this application's process that
+     *                   belongs to this {@code ActivityStack}
+     * @param isEmpty Indicates whether there's any {@link Activity} running in this
+     *                {@code ActivityStack}
+     * @param token The token to identify this {@code ActivityStack}
+     * Since {@link WindowExtensions#VENDOR_API_LEVEL_3}
+     */
+    ActivityStack(@NonNull List<Activity> activities, boolean isEmpty, @NonNull IBinder token) {
+        Objects.requireNonNull(activities);
+        Objects.requireNonNull(token);
+        mActivities = new ArrayList<>(activities);
+        mIsEmpty = isEmpty;
+        mToken = token;
+    }
+
+    /**
+     * @deprecated Use the {@link WindowExtensions#VENDOR_API_LEVEL_3} version.
+     * Since {@link WindowExtensions#VENDOR_API_LEVEL_1}
+     */
+    @Deprecated
+    ActivityStack(@NonNull List<Activity> activities, boolean isEmpty) {
+        this(activities, isEmpty, INVALID_ACTIVITY_STACK_TOKEN);
+    }
+
+    /**
+     * Returns {@link Activity Activities} in this application's process that belongs to this
+     * ActivityStack.
+     * <p>
+     * Note that Activities that are running in other processes are not reported in the returned
+     * Activity list. They can be in any position in terms of ordering relative to the activities
+     * in the list.
+     * </p>
+     */
+    @NonNull
+    public List<Activity> getActivities() {
+        return new ArrayList<>(mActivities);
+    }
+
+    /**
+     * Returns {@code true} if there's no {@link Activity} running in this ActivityStack.
+     * <p>
+     * Note that {@link #getActivities()} only report Activity in the process used to create this
+     * ActivityStack. That said, if this ActivityStack only contains activities from another
+     * process, {@link #getActivities()} will return empty list, while this method will return
+     * {@code false}.
+     * </p>
+     */
+    public boolean isEmpty() {
+        return mIsEmpty;
+    }
+
+    /**
+     * Returns a token uniquely identifying the container.
+     * Since {@link WindowExtensions#VENDOR_API_LEVEL_3}
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    @NonNull
+    public IBinder getToken() {
+        return mToken;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof ActivityStack)) return false;
+        ActivityStack that = (ActivityStack) o;
+        return mActivities.equals(that.mActivities)
+                && mIsEmpty == that.mIsEmpty
+                && mToken.equals(that.mToken);
+    }
+
+    @Override
+    public int hashCode() {
+        int result = (mIsEmpty ? 1 : 0);
+        result = result * 31 + mActivities.hashCode();
+        result = result * 31 + mToken.hashCode();
+        return result;
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        return "ActivityStack{" + "mActivities=" + mActivities
+                + ", mIsEmpty=" + mIsEmpty
+                + ", mToken=" + mToken
+                + '}';
+    }
+}
diff --git a/window_extensions/java/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/EmbeddingRule.java b/window_extensions/java/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/EmbeddingRule.java
new file mode 100644
index 0000000..9333bdc
--- /dev/null
+++ b/window_extensions/java/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/EmbeddingRule.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 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 androidx.window.extensions.embedding;
+
+import androidx.annotation.Nullable;
+import androidx.window.extensions.core.util.function.Function;
+
+import java.util.Objects;
+
+/**
+ * Base interface for activity embedding rules. Used to group different types of rules together when
+ * updating from the core library.
+ */
+public abstract class EmbeddingRule {
+    @Nullable
+    private final String mTag;
+
+    EmbeddingRule(@Nullable String tag) {
+        mTag = tag;
+    }
+
+    /**
+     * A unique string to identify this {@link EmbeddingRule}.
+     * The suggested usage is to set the tag in the corresponding rule builder to be able to
+     * differentiate between different rules in {@link SplitAttributes} calculator function. For
+     * example, it can be used to compute the {@link SplitAttributes} for the specific
+     * {@link SplitRule} in the {@link Function} set with
+     * {@link ActivityEmbeddingComponent#setSplitAttributesCalculator(Function)}.
+     *
+     * Since {@link androidx.window.extensions.WindowExtensions#VENDOR_API_LEVEL_2}
+     */
+    @Nullable
+    public String getTag() {
+        return mTag;
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (this == other) return true;
+        if (!(other instanceof EmbeddingRule)) return false;
+        final EmbeddingRule otherRule = (EmbeddingRule) other;
+        return Objects.equals(mTag, otherRule.mTag);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(mTag);
+    }
+}
diff --git a/window_extensions/java/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/SplitAttributes.java b/window_extensions/java/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/SplitAttributes.java
new file mode 100644
index 0000000..e00e910
--- /dev/null
+++ b/window_extensions/java/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/SplitAttributes.java
@@ -0,0 +1,587 @@
+/*
+ * Copyright 2022 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 androidx.window.extensions.embedding;
+
+import static androidx.annotation.RestrictTo.Scope.LIBRARY;
+import static androidx.window.extensions.embedding.SplitAttributes.LayoutDirection.BOTTOM_TO_TOP;
+import static androidx.window.extensions.embedding.SplitAttributes.LayoutDirection.LEFT_TO_RIGHT;
+import static androidx.window.extensions.embedding.SplitAttributes.LayoutDirection.LOCALE;
+import static androidx.window.extensions.embedding.SplitAttributes.LayoutDirection.RIGHT_TO_LEFT;
+import static androidx.window.extensions.embedding.SplitAttributes.LayoutDirection.TOP_TO_BOTTOM;
+
+import android.annotation.SuppressLint;
+import android.graphics.Color;
+
+import androidx.annotation.ColorInt;
+import androidx.annotation.FloatRange;
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+import androidx.window.extensions.core.util.function.Function;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Attributes that describe how the parent window (typically the activity task
+ * window) is split between the primary and secondary activity containers,
+ * including:
+ * <ul>
+ *     <li>Split type -- Categorizes the split and specifies the sizes of the
+ *         primary and secondary activity containers relative to the parent
+ *         bounds</li>
+ *     <li>Layout direction -- Specifies whether the parent window is split
+ *         vertically or horizontally and in which direction the primary and
+ *         secondary containers are respectively positioned (left to right,
+ *         right to left, top to bottom, and so forth)</li>
+ *     <li>Animation background color -- The color of the background during
+ *         animation of the split involving this {@code SplitAttributes} object
+ *         if the animation requires a background</li>
+ * </ul>
+ *
+ * <p>Attributes can be configured by:
+ * <ul>
+ *     <li>Setting the default {@code SplitAttributes} using
+ *         {@link SplitPairRule.Builder#setDefaultSplitAttributes} or
+ *         {@link SplitPlaceholderRule.Builder#setDefaultSplitAttributes}.</li>
+ *     <li>Using {@link ActivityEmbeddingComponent#setSplitAttributesCalculator(Function)} to set
+ *         the callback to customize the {@code SplitAttributes} for a given device and window
+ *         state.</li>
+ * </ul>
+ *
+ * @see SplitAttributes.SplitType
+ * @see SplitAttributes.LayoutDirection
+ * Since {@link androidx.window.extensions.WindowExtensions#VENDOR_API_LEVEL_2}
+ */
+public class SplitAttributes {
+
+    /**
+     * The default value for animation background color, which means to use the current theme window
+     * background color.
+     *
+     * Only opaque color is supported, so {@code 0} is used as the default. Any other non-opaque
+     * color will be treated as the default.
+     *
+     * @see Builder#setAnimationBackgroundColor(int)
+     */
+    @ColorInt
+    @RestrictTo(LIBRARY)
+    public static final int DEFAULT_ANIMATION_BACKGROUND_COLOR = 0;
+
+    /**
+     * The type of window split, which defines the proportion of the parent
+     * window occupied by the primary and secondary activity containers.
+     */
+    public static class SplitType {
+        @NonNull
+        private final String mDescription;
+
+        SplitType(@NonNull String description) {
+            mDescription = description;
+        }
+
+        @Override
+        public int hashCode() {
+            return mDescription.hashCode();
+        }
+
+        @Override
+        public boolean equals(@Nullable Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (!(obj instanceof SplitType)) {
+                return false;
+            }
+            final SplitType that = (SplitType) obj;
+            return mDescription.equals(that.mDescription);
+        }
+
+        @NonNull
+        @Override
+        public String toString() {
+            return mDescription;
+        }
+
+        @SuppressLint("Range") // The range is covered.
+        @NonNull
+        static SplitType createSplitTypeFromLegacySplitRatio(
+                @FloatRange(from = 0.0, to = 1.0) float splitRatio) {
+            // Treat 0.0 and 1.0 as ExpandContainerSplitType because it means the parent container
+            // is filled with secondary or primary container.
+            if (splitRatio == 0.0 || splitRatio == 1.0) {
+                return new ExpandContainersSplitType();
+            }
+            return new RatioSplitType(splitRatio);
+        }
+
+        /**
+         * A window split that's based on the ratio of the size of the primary
+         * container to the size of the parent window.
+         *
+         * <p>Values in the non-inclusive range (0.0, 1.0) define the size of
+         * the primary container relative to the size of the parent window:
+         * <ul>
+         *     <li>0.5 -- Primary container occupies half of the parent
+         *         window; secondary container, the other half</li>
+         *     <li>Greater than 0.5 -- Primary container occupies a larger
+         *         proportion of the parent window than the secondary
+         *         container</li>
+         *     <li>Less than 0.5 -- Primary container occupies a smaller
+         *         proportion of the parent window than the secondary
+         *         container</li>
+         * </ul>
+         */
+        public static final class RatioSplitType extends SplitType {
+            @FloatRange(from = 0.0, to = 1.0, fromInclusive = false, toInclusive = false)
+            private final float mRatio;
+
+            /**
+             * Creates an instance of this {@code RatioSplitType}.
+             *
+             * @param ratio The proportion of the parent window occupied by the
+             *     primary container of the split. Can be a value in the
+             *     non-inclusive range (0.0, 1.0). Use
+             *     {@link SplitType.ExpandContainersSplitType} to create a split
+             *     type that occupies the entire parent window.
+             */
+            public RatioSplitType(
+                    @FloatRange(from = 0.0, to = 1.0, fromInclusive = false, toInclusive = false)
+                    float ratio) {
+                super("ratio:" + ratio);
+                if (ratio <= 0.0f || ratio >= 1.0f) {
+                    throw new IllegalArgumentException("Ratio must be in range (0.0, 1.0). "
+                            + " Use SplitType.ExpandContainersSplitType() instead of 0 or 1.");
+                }
+                mRatio = ratio;
+            }
+
+            /**
+             * Gets the proportion of the parent window occupied by the primary
+             * activity container of the split.
+             *
+             * @return The proportion of the split occupied by the primary
+             *     container.
+             */
+            @FloatRange(from = 0.0, to = 1.0, fromInclusive = false, toInclusive = false)
+            public float getRatio() {
+                return mRatio;
+            }
+
+            /**
+             * Creates a split type in which the primary and secondary
+             * containers occupy equal portions of the parent window.
+             *
+             * Serves as the default {@link SplitType} if
+             * {@link SplitAttributes.Builder#setSplitType(SplitType)} is not
+             * specified.
+             *
+             * @return A {@code RatioSplitType} in which the activity containers
+             *     occupy equal portions of the parent window.
+             */
+            @NonNull
+            public static RatioSplitType splitEqually() {
+                return new RatioSplitType(0.5f);
+            }
+        }
+
+        /**
+         * A parent window split in which the split ratio conforms to the
+         * position of a hinge or separating fold in the device display.
+         *
+         * The split type is created only if:
+         * <ul>
+         *     <li>The host task is not in multi-window mode (e.g.,
+         *         split-screen mode or picture-in-picture mode)</li>
+         *     <li>The device has a hinge or separating fold reported by
+         *         [androidx.window.layout.FoldingFeature.isSeparating]</li>
+         *     <li>The hinge or separating fold orientation matches how the
+         *         parent bounds are split:
+         *         <ul>
+         *             <li>The hinge or fold orientation is vertical, and
+         *                 the task bounds are also split vertically
+         *                 (containers are side by side)</li>
+         *             <li>The hinge or fold orientation is horizontal, and
+         *                 the task bounds are also split horizontally
+         *                 (containers are top and bottom)</li>
+         *         </ul>
+         *     </li>
+         * </ul>
+         *
+         * Otherwise, the type falls back to the {@code SplitType} returned by
+         * {@link #getFallbackSplitType()}.
+         */
+        public static final class HingeSplitType extends SplitType {
+            @NonNull
+            private final SplitType mFallbackSplitType;
+
+            /**
+             * Creates an instance of this {@code HingeSplitType}.
+             *
+             * @param fallbackSplitType The split type to use if a split based
+             *     on the device hinge or separating fold cannot be determined.
+             *     Can be a {@link RatioSplitType} or
+             *     {@link ExpandContainersSplitType}.
+             */
+            public HingeSplitType(@NonNull SplitType fallbackSplitType) {
+                super("hinge, fallbackType=" + fallbackSplitType);
+                mFallbackSplitType = fallbackSplitType;
+            }
+
+            /**
+             * Returns the fallback {@link SplitType} if a split based on the
+             * device hinge or separating fold cannot be determined.
+             */
+            @NonNull
+            public SplitType getFallbackSplitType() {
+                return mFallbackSplitType;
+            }
+        }
+
+        /**
+         * A window split in which the primary and secondary activity containers
+         * each occupy the entire parent window.
+         *
+         * The secondary container overlays the primary container.
+         */
+        public static final class ExpandContainersSplitType extends SplitType {
+
+            /**
+             * Creates an instance of this {@code ExpandContainersSplitType}.
+             */
+            public ExpandContainersSplitType() {
+                super("expandContainers");
+            }
+        }
+    }
+
+    /**
+     * The layout direction of the primary and secondary activity containers.
+     */
+    public static final class LayoutDirection {
+
+        /**
+         * Specifies that the parent bounds are split vertically (side to side).
+         *
+         * Places the primary container in the left portion of the parent
+         * window, and the secondary container in the right portion.
+         *
+         * A possible return value of {@link SplitType#getLayoutDirection()}.
+         */
+         //
+         // -------------------------
+         // |           |           |
+         // |  Primary  | Secondary |
+         // |           |           |
+         // -------------------------
+         //
+         // Must match {@link LayoutDirection#LTR} for backwards compatibility
+         // with prior versions of Extensions.
+        public static final int LEFT_TO_RIGHT = 0;
+
+        /**
+         * Specifies that the parent bounds are split vertically (side to
+         * side).
+         *
+         * Places the primary container in the right portion of the parent
+         * window, and the secondary container in the left portion.
+         *
+         * A possible return value of {@link SplitType#getLayoutDirection()}.
+         */
+         // -------------------------
+         // |           |           |
+         // | Secondary |  Primary  |
+         // |           |           |
+         // -------------------------
+         //
+         // Must match {@link LayoutDirection#RTL} for backwards compatibility
+         // with prior versions of Extensions.
+        public static final int RIGHT_TO_LEFT = 1;
+
+        /**
+         * Specifies that the parent bounds are split vertically (side to side).
+         *
+         * The direction of the primary and secondary containers is deduced from
+         * the locale as either {@link #LEFT_TO_RIGHT} or
+         * {@link #RIGHT_TO_LEFT}.
+         *
+         * A possible return value of {@link SplitType#getLayoutDirection()}.
+         */
+         // Must match {@link LayoutDirection#LOCALE} for backwards
+         // compatibility with prior versions of Extensions.
+        public static final int LOCALE = 3;
+
+        /**
+         * Specifies that the parent bounds are split horizontally (top and
+         * bottom).
+         *
+         * Places the primary container in the top portion of the parent window,
+         * and the secondary container in the bottom portion.
+         *
+         * If the horizontal layout direction is not supported on the device,
+         * layout direction falls back to {@link #LOCALE}.
+         *
+         * A possible return value of {@link SplitType#getLayoutDirection()}.
+         */
+         // -------------
+         // |           |
+         // |  Primary  |
+         // |           |
+         // -------------
+         // |           |
+         // | Secondary |
+         // |           |
+         // -------------
+        public static final int TOP_TO_BOTTOM = 4;
+
+        /**
+         * Specifies that the parent bounds are split horizontally (top and
+         * bottom).
+         *
+         * Places the primary container in the bottom portion of the parent
+         * window, and the secondary container in the top portion.
+         *
+         * If the horizontal layout direction is not supported on the device,
+         * layout direction falls back to {@link #LOCALE}.
+         *
+         * A possible return value of {@link SplitType#getLayoutDirection()}.
+         */
+         // -------------
+         // |           |
+         // | Secondary |
+         // |           |
+         // -------------
+         // |           |
+         // |  Primary  |
+         // |           |
+         // -------------
+        public static final int BOTTOM_TO_TOP = 5;
+
+        private LayoutDirection() {}
+    }
+
+    @IntDef({LEFT_TO_RIGHT, RIGHT_TO_LEFT, LOCALE, TOP_TO_BOTTOM, BOTTOM_TO_TOP})
+    @Retention(RetentionPolicy.SOURCE)
+    @interface ExtLayoutDirection {}
+
+    @ExtLayoutDirection
+    private final int mLayoutDirection;
+
+    private final SplitType mSplitType;
+
+    @ColorInt
+    private final int mAnimationBackgroundColor;
+
+    /**
+     * Creates an instance of this {@code SplitAttributes}.
+     *
+     * @param splitType The type of split. See
+     *     {@link SplitAttributes.SplitType}.
+     * @param layoutDirection The layout direction of the split, such as left to
+     *     right or top to bottom. See {@link SplitAttributes.LayoutDirection}.
+     * @param animationBackgroundColor The {@link ColorInt} to use for the
+     *     background color during animation of the split involving this
+     *     {@code SplitAttributes} object if the animation requires a
+     *     background.
+     */
+    SplitAttributes(@NonNull SplitType splitType, @ExtLayoutDirection int layoutDirection,
+            @ColorInt int animationBackgroundColor) {
+        mSplitType = splitType;
+        mLayoutDirection = layoutDirection;
+        mAnimationBackgroundColor = animationBackgroundColor;
+    }
+
+    /**
+     * Gets the layout direction of the split.
+     *
+     * @return The layout direction of the split.
+     */
+    @ExtLayoutDirection
+    public int getLayoutDirection() {
+        return mLayoutDirection;
+    }
+
+    /**
+     * Gets the split type.
+     *
+     * @return The split type.
+     */
+    @NonNull
+    public SplitType getSplitType() {
+        return mSplitType;
+    }
+
+    /**
+     * Gets the {@link ColorInt} to use for the background color during the
+     * animation of the split involving this {@code SplitAttributes} object.
+     *
+     * The default is {@link #DEFAULT_ANIMATION_BACKGROUND_COLOR}, which means
+     * to use the current theme window background color.
+     *
+     * @return The animation background {@code ColorInt}.
+     */
+    @ColorInt
+    @RestrictTo(LIBRARY)
+    public int getAnimationBackgroundColor() {
+        return mAnimationBackgroundColor;
+    }
+
+    /**
+     * Builder for creating an instance of {@link SplitAttributes}.
+     *
+     * - The default split type is an equal split between primary and secondary containers.
+     * - The default layout direction is based on locale.
+     * - The default animation background color is to use the current theme window background color.
+     */
+    public static final class Builder {
+        @NonNull
+        private SplitType mSplitType =  new SplitType.RatioSplitType(0.5f);
+        @ExtLayoutDirection
+        private int mLayoutDirection = LOCALE;
+
+        @ColorInt
+        private int mAnimationBackgroundColor = 0;
+
+        /**
+         * Sets the split type attribute.
+         *
+         * The default is an equal split between primary and secondary
+         * containers (see {@link SplitType.RatioSplitType#splitEqually()}).
+         *
+         * @param splitType The split type attribute.
+         * @return This {@code Builder}.
+         */
+        @NonNull
+        public Builder setSplitType(@NonNull SplitType splitType) {
+            mSplitType = splitType;
+            return this;
+        }
+
+        /**
+         * Sets the split layout direction attribute.
+         *
+         * The default is based on locale.
+         *
+         * Must be one of:
+         * <ul>
+         *     <li>{@link LayoutDirection#LOCALE}</li>
+         *     <li>{@link LayoutDirection#LEFT_TO_RIGHT}</li>
+         *     <li>{@link LayoutDirection#RIGHT_TO_LEFT}</li>
+         *     <li>{@link LayoutDirection#TOP_TO_BOTTOM}</li>
+         *     <li>{@link LayoutDirection#BOTTOM_TO_TOP}</li>
+         * </ul>
+         *
+         * @param layoutDirection The layout direction attribute.
+         * @return This {@code Builder}.
+         */
+        @SuppressLint("WrongConstant") // To be compat with android.util.LayoutDirection APIs
+        @NonNull
+        public Builder setLayoutDirection(@ExtLayoutDirection int layoutDirection) {
+            mLayoutDirection = layoutDirection;
+            return this;
+        }
+
+        /**
+         * Sets the {@link ColorInt} to use for the background during the
+         * animation of the split involving this {@code SplitAttributes} object
+         * if the animation requires a background.
+         *
+         * Only opaque color is supported.
+         *
+         * The default value is {@link #DEFAULT_ANIMATION_BACKGROUND_COLOR}, which
+         * means to use the current theme window background color. Any non-opaque
+         * animation color will be treated as
+         * {@link #DEFAULT_ANIMATION_BACKGROUND_COLOR}.
+         *
+         * @param color A packed color int of the form {@code AARRGGBB} for the
+         *              animation background color.
+         * @return This {@code Builder}.
+         */
+        @NonNull
+        @RestrictTo(LIBRARY)
+        public Builder setAnimationBackgroundColor(@ColorInt int color) {
+            // Any non-opaque color will be treated as the default.
+            mAnimationBackgroundColor = Color.alpha(color) != 255
+                    ? DEFAULT_ANIMATION_BACKGROUND_COLOR
+                    : color;
+            return this;
+        }
+
+        /**
+         * Builds a {@link SplitAttributes} instance with the attributes
+         * specified by {@link #setSplitType}, {@link #setLayoutDirection}, and
+         * {@link #setAnimationBackgroundColor}.
+         *
+         * @return The new {@code SplitAttributes} instance.
+         */
+        @NonNull
+        public SplitAttributes build() {
+            return new SplitAttributes(mSplitType, mLayoutDirection, mAnimationBackgroundColor);
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        int result = mSplitType.hashCode();
+        result = result * 31 + mLayoutDirection;
+        result = result * 31 + mAnimationBackgroundColor;
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (other == this) {
+            return true;
+        }
+        if (!(other instanceof SplitAttributes)) {
+            return false;
+        }
+        final SplitAttributes otherAttributes = (SplitAttributes) other;
+        return mLayoutDirection == otherAttributes.mLayoutDirection
+                && mSplitType.equals(otherAttributes.mSplitType)
+                && mAnimationBackgroundColor == otherAttributes.mAnimationBackgroundColor;
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        return SplitAttributes.class.getSimpleName() + "{"
+                + "layoutDir=" + layoutDirectionToString()
+                + ", ratio=" + mSplitType
+                + ", animationBgColor=" + Integer.toHexString(mAnimationBackgroundColor)
+                + "}";
+    }
+
+    @NonNull
+    private String layoutDirectionToString() {
+        switch(mLayoutDirection) {
+            case LEFT_TO_RIGHT:
+                return "LEFT_TO_RIGHT";
+            case RIGHT_TO_LEFT:
+                return "RIGHT_TO_LEFT";
+            case LOCALE:
+                return "LOCALE";
+            case TOP_TO_BOTTOM:
+                return "TOP_TO_BOTTOM";
+            case BOTTOM_TO_TOP:
+                return "BOTTOM_TO_TOP";
+            default:
+                throw new IllegalArgumentException("Invalid layout direction:" + mLayoutDirection);
+        }
+    }
+}
diff --git a/window_extensions/java/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/SplitAttributesCalculatorParams.java b/window_extensions/java/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/SplitAttributesCalculatorParams.java
new file mode 100644
index 0000000..80f98fe
--- /dev/null
+++ b/window_extensions/java/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/SplitAttributesCalculatorParams.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2022 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 androidx.window.extensions.embedding;
+
+import android.content.res.Configuration;
+import android.os.Build;
+import android.view.WindowMetrics;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+import androidx.window.extensions.layout.WindowLayoutInfo;
+
+/**
+ * The parameter container used to report the current device and window state in
+ * {@link ActivityEmbeddingComponent#setSplitAttributesCalculator(
+ * androidx.window.extensions.core.util.function.Function)} and references the corresponding
+ * {@link SplitRule} by {@link #getSplitRuleTag()} if {@link SplitRule#getTag()} is specified.
+ *
+ * @see ActivityEmbeddingComponent#clearSplitAttributesCalculator()
+ * Since {@link androidx.window.extensions.WindowExtensions#VENDOR_API_LEVEL_2}
+ */
+public class SplitAttributesCalculatorParams {
+    @NonNull
+    private final WindowMetrics mParentWindowMetrics;
+    @NonNull
+    private final Configuration mParentConfiguration;
+    @NonNull
+    private final WindowLayoutInfo mParentWindowLayoutInfo;
+    @NonNull
+    private final SplitAttributes mDefaultSplitAttributes;
+    private final boolean mAreDefaultConstraintsSatisfied;
+    @Nullable
+    private final String mSplitRuleTag;
+
+    /** Returns the parent container's {@link WindowMetrics} */
+    @NonNull
+    public WindowMetrics getParentWindowMetrics() {
+        return mParentWindowMetrics;
+    }
+
+    /** Returns the parent container's {@link Configuration} */
+    @NonNull
+    public Configuration getParentConfiguration() {
+        return new Configuration(mParentConfiguration);
+    }
+
+    /**
+     * Returns the {@link SplitRule#getDefaultSplitAttributes()}. It could be from
+     * {@link SplitRule} Builder APIs
+     * ({@link SplitPairRule.Builder#setDefaultSplitAttributes(SplitAttributes)} or
+     * {@link SplitPlaceholderRule.Builder#setDefaultSplitAttributes(SplitAttributes)}) or from
+     * the {@code splitRatio} and {@code splitLayoutDirection} attributes from static rule
+     * definitions.
+     */
+    @NonNull
+    public SplitAttributes getDefaultSplitAttributes() {
+        return mDefaultSplitAttributes;
+    }
+
+    /**
+     * Returns whether the {@link #getParentWindowMetrics()} satisfies the dimensions and aspect
+     * ratios requirements specified in the {@link androidx.window.embedding.SplitRule}, which
+     * are:
+     *  - {@link androidx.window.embedding.SplitRule#minWidthDp}
+     *  - {@link androidx.window.embedding.SplitRule#minHeightDp}
+     *  - {@link androidx.window.embedding.SplitRule#minSmallestWidthDp}
+     *  - {@link androidx.window.embedding.SplitRule#maxAspectRatioInPortrait}
+     *  - {@link androidx.window.embedding.SplitRule#maxAspectRatioInLandscape}
+     */
+    public boolean areDefaultConstraintsSatisfied() {
+        return mAreDefaultConstraintsSatisfied;
+    }
+
+    /** Returns the parent container's {@link WindowLayoutInfo} */
+    @NonNull
+    public WindowLayoutInfo getParentWindowLayoutInfo() {
+        return mParentWindowLayoutInfo;
+    }
+
+    /**
+     * Returns {@link SplitRule#getTag()} to apply the {@link SplitAttributes} result if it was
+     * set.
+     */
+    @Nullable
+    public String getSplitRuleTag() {
+        return mSplitRuleTag;
+    }
+
+    SplitAttributesCalculatorParams(
+            @NonNull WindowMetrics parentWindowMetrics,
+            @NonNull Configuration parentConfiguration,
+            @NonNull WindowLayoutInfo parentWindowLayoutInfo,
+            @NonNull SplitAttributes defaultSplitAttributes,
+            boolean areDefaultConstraintsSatisfied,
+            @Nullable String splitRuleTag
+    ) {
+        mParentWindowMetrics = parentWindowMetrics;
+        mParentConfiguration = parentConfiguration;
+        mParentWindowLayoutInfo = parentWindowLayoutInfo;
+        mDefaultSplitAttributes = defaultSplitAttributes;
+        mAreDefaultConstraintsSatisfied = areDefaultConstraintsSatisfied;
+        mSplitRuleTag = splitRuleTag;
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        return getClass().getSimpleName() + ":{"
+                + "windowMetrics=" + windowMetricsToString(mParentWindowMetrics)
+                + ", configuration=" + mParentConfiguration
+                + ", windowLayoutInfo=" + mParentWindowLayoutInfo
+                + ", defaultSplitAttributes=" + mDefaultSplitAttributes
+                + ", areDefaultConstraintsSatisfied=" + mAreDefaultConstraintsSatisfied
+                + ", tag=" + mSplitRuleTag + "}";
+    }
+
+    private static String windowMetricsToString(@NonNull WindowMetrics windowMetrics) {
+        // TODO(b/187712731): Use WindowMetrics#toString after it's implemented in U.
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+            return Api30Impl.windowMetricsToString(windowMetrics);
+        }
+        throw new UnsupportedOperationException("WindowMetrics didn't exist in R.");
+    }
+
+    @RequiresApi(30)
+    private static final class Api30Impl {
+        static String windowMetricsToString(@NonNull WindowMetrics windowMetrics) {
+            return WindowMetrics.class.getSimpleName() + ":{"
+                    + "bounds=" + windowMetrics.getBounds()
+                    + ", windowInsets=" + windowMetrics.getWindowInsets()
+                    + "}";
+        }
+    }
+}
diff --git a/window_extensions/java/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/SplitInfo.java b/window_extensions/java/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/SplitInfo.java
new file mode 100644
index 0000000..bac42a4
--- /dev/null
+++ b/window_extensions/java/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/SplitInfo.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright 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 androidx.window.extensions.embedding;
+
+import android.os.Binder;
+import android.os.IBinder;
+
+import androidx.annotation.NonNull;
+import androidx.window.extensions.WindowExtensions;
+import androidx.window.extensions.embedding.SplitAttributes.SplitType;
+
+import java.util.Objects;
+
+/** Describes a split of two containers with activities. */
+public class SplitInfo {
+
+    /** Only used for compatibility with the deprecated constructor. */
+    private static final IBinder INVALID_SPLIT_INFO_TOKEN = new Binder();
+
+    @NonNull
+    private final ActivityStack mPrimaryActivityStack;
+    @NonNull
+    private final ActivityStack mSecondaryActivityStack;
+    @NonNull
+    private final SplitAttributes mSplitAttributes;
+
+    @NonNull
+    private final IBinder mToken;
+
+    /**
+     * The {@code SplitInfo} constructor
+     *
+     * @param primaryActivityStack The primary {@link ActivityStack}
+     * @param secondaryActivityStack The secondary {@link ActivityStack}
+     * @param splitAttributes The current {@link SplitAttributes} of this split pair
+     * @param token The token to identify this split pair
+     * Since {@link WindowExtensions#VENDOR_API_LEVEL_3}
+     */
+    SplitInfo(@NonNull ActivityStack primaryActivityStack,
+            @NonNull ActivityStack secondaryActivityStack,
+            @NonNull SplitAttributes splitAttributes,
+            @NonNull IBinder token) {
+        Objects.requireNonNull(primaryActivityStack);
+        Objects.requireNonNull(secondaryActivityStack);
+        Objects.requireNonNull(splitAttributes);
+        Objects.requireNonNull(token);
+        mPrimaryActivityStack = primaryActivityStack;
+        mSecondaryActivityStack = secondaryActivityStack;
+        mSplitAttributes = splitAttributes;
+        mToken = token;
+    }
+
+    /**
+     * @deprecated Use the {@link WindowExtensions#VENDOR_API_LEVEL_3} version.
+     * Since {@link WindowExtensions#VENDOR_API_LEVEL_1}
+     */
+    @Deprecated
+    SplitInfo(@NonNull ActivityStack primaryActivityStack,
+            @NonNull ActivityStack secondaryActivityStack,
+            @NonNull SplitAttributes splitAttributes) {
+        this(primaryActivityStack, secondaryActivityStack, splitAttributes,
+                INVALID_SPLIT_INFO_TOKEN);
+    }
+
+    @NonNull
+    public ActivityStack getPrimaryActivityStack() {
+        return mPrimaryActivityStack;
+    }
+
+    @NonNull
+    public ActivityStack getSecondaryActivityStack() {
+        return mSecondaryActivityStack;
+    }
+
+    /**
+     * @deprecated Use {@link #getSplitAttributes()} starting with
+     * {@link WindowExtensions#VENDOR_API_LEVEL_2}. Only used if {@link #getSplitAttributes()}
+     * can't be called on {@link WindowExtensions#VENDOR_API_LEVEL_1}.
+     */
+    @Deprecated
+    public float getSplitRatio() {
+        final SplitType splitType = mSplitAttributes.getSplitType();
+        if (splitType instanceof SplitType.RatioSplitType) {
+            return ((SplitType.RatioSplitType) splitType).getRatio();
+        } else { // Fallback to use 0.0 because the WM Jetpack may not support HingeSplitType.
+            return 0.0f;
+        }
+    }
+
+    /**
+     * Returns the {@link SplitAttributes} of this split.
+     * Since {@link androidx.window.extensions.WindowExtensions#VENDOR_API_LEVEL_2}
+     */
+    @NonNull
+    public SplitAttributes getSplitAttributes() {
+        return mSplitAttributes;
+    }
+
+    /**
+     * Returns a token uniquely identifying the container.
+     * Since {@link WindowExtensions#VENDOR_API_LEVEL_3}
+     */
+    @NonNull
+    public IBinder getToken() {
+        return mToken;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof SplitInfo)) return false;
+        SplitInfo that = (SplitInfo) o;
+        return mSplitAttributes.equals(that.mSplitAttributes) && mPrimaryActivityStack.equals(
+                that.mPrimaryActivityStack) && mSecondaryActivityStack.equals(
+                that.mSecondaryActivityStack) && mToken.equals(that.mToken);
+    }
+
+    @Override
+    public int hashCode() {
+        int result = mPrimaryActivityStack.hashCode();
+        result = result * 31 + mSecondaryActivityStack.hashCode();
+        result = result * 31 + mSplitAttributes.hashCode();
+        result = result * 31 + mToken.hashCode();
+        return result;
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        return "SplitInfo{"
+                + "mPrimaryActivityStack=" + mPrimaryActivityStack
+                + ", mSecondaryActivityStack=" + mSecondaryActivityStack
+                + ", mSplitAttributes=" + mSplitAttributes
+                + ", mToken=" + mToken
+                + '}';
+    }
+}
diff --git a/window_extensions/java/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/SplitPairRule.java b/window_extensions/java/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/SplitPairRule.java
new file mode 100644
index 0000000..ada0637
--- /dev/null
+++ b/window_extensions/java/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/SplitPairRule.java
@@ -0,0 +1,319 @@
+/*
+ * Copyright 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 androidx.window.extensions.embedding;
+
+import static androidx.window.extensions.embedding.SplitAttributes.SplitType.createSplitTypeFromLegacySplitRatio;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Build;
+import android.util.Pair;
+import android.view.WindowMetrics;
+
+import androidx.annotation.FloatRange;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+import androidx.window.extensions.WindowExtensions;
+import androidx.window.extensions.core.util.function.Predicate;
+
+import java.util.Objects;
+
+/**
+ * Split configuration rules for activity pairs.
+ */
+public class SplitPairRule extends SplitRule {
+    @NonNull
+    private final Predicate<Pair<Activity, Activity>> mActivityPairPredicate;
+    @NonNull
+    private final Predicate<Pair<Activity, Intent>> mActivityIntentPredicate;
+    @SplitFinishBehavior
+    private final int mFinishPrimaryWithSecondary;
+    @SplitFinishBehavior
+    private final int mFinishSecondaryWithPrimary;
+    private final boolean mClearTop;
+
+    SplitPairRule(@NonNull SplitAttributes defaultSplitAttributes,
+            @SplitFinishBehavior int finishPrimaryWithSecondary,
+            @SplitFinishBehavior int finishSecondaryWithPrimary, boolean clearTop,
+            @NonNull Predicate<Pair<Activity, Activity>> activityPairPredicate,
+            @NonNull Predicate<Pair<Activity, Intent>> activityIntentPredicate,
+            @NonNull Predicate<WindowMetrics> parentWindowMetricsPredicate,
+            @Nullable String tag) {
+        super(parentWindowMetricsPredicate, defaultSplitAttributes, tag);
+        mActivityPairPredicate = activityPairPredicate;
+        mActivityIntentPredicate = activityIntentPredicate;
+        mFinishPrimaryWithSecondary = finishPrimaryWithSecondary;
+        mFinishSecondaryWithPrimary = finishSecondaryWithPrimary;
+        mClearTop = clearTop;
+    }
+
+    /**
+     * Checks if the rule is applicable to the provided activities.
+     */
+    @SuppressLint("ClassVerificationFailure") // Only called by Extensions implementation on device.
+    @RequiresApi(api = Build.VERSION_CODES.N)
+    public boolean matchesActivityPair(@NonNull Activity primaryActivity,
+            @NonNull Activity secondaryActivity) {
+        return mActivityPairPredicate.test(new Pair<>(primaryActivity, secondaryActivity));
+    }
+
+    /**
+     * Checks if the rule is applicable to the provided primary activity and secondary activity
+     * intent.
+     */
+    @SuppressLint("ClassVerificationFailure") // Only called by Extensions implementation on device.
+    @RequiresApi(api = Build.VERSION_CODES.N)
+    public boolean matchesActivityIntentPair(@NonNull Activity primaryActivity,
+            @NonNull Intent secondaryActivityIntent) {
+        return mActivityIntentPredicate.test(new Pair<>(primaryActivity, secondaryActivityIntent));
+    }
+
+    /**
+     * Determines what happens with the primary container when all activities are finished in the
+     * associated secondary container.
+     */
+    @SplitFinishBehavior
+    public int getFinishPrimaryWithSecondary() {
+        return mFinishPrimaryWithSecondary;
+    }
+
+    /**
+     * Determines what happens with the secondary container when all activities are finished in the
+     * associated primary container.
+     */
+    @SplitFinishBehavior
+    public int getFinishSecondaryWithPrimary() {
+        return mFinishSecondaryWithPrimary;
+    }
+
+    /**
+     * If there is an existing split with the same primary container, indicates whether the
+     * existing secondary container and all activities in it should be destroyed. Otherwise the new
+     * secondary will appear on top. Defaults to "true".
+     */
+    public boolean shouldClearTop() {
+        return mClearTop;
+    }
+
+    /**
+     * Builder for {@link SplitPairRule}.
+     */
+    public static final class Builder {
+        @NonNull
+        private final Predicate<Pair<Activity, Activity>> mActivityPairPredicate;
+        @NonNull
+        private final Predicate<Pair<Activity, Intent>> mActivityIntentPredicate;
+        @NonNull
+        private final Predicate<WindowMetrics> mParentWindowMetricsPredicate;
+        // Keep for backward compatibility
+        @FloatRange(from = 0.0, to = 1.0)
+        private float mSplitRatio;
+        // Keep for backward compatibility
+        @SplitAttributes.ExtLayoutDirection
+        private int mLayoutDirection;
+        private SplitAttributes mDefaultSplitAttributes;
+        private boolean mClearTop;
+        @SplitFinishBehavior
+        private int mFinishPrimaryWithSecondary;
+        @SplitFinishBehavior
+        private int mFinishSecondaryWithPrimary;
+        @Nullable
+        private String mTag;
+
+        /**
+         * @deprecated Use {@link #Builder(Predicate, Predicate, Predicate)} starting with
+         * {@link WindowExtensions#VENDOR_API_LEVEL_2}. Only used if
+         * {@link #Builder(Predicate, Predicate, Predicate)} can't be called on
+         * {@link WindowExtensions#VENDOR_API_LEVEL_1}.
+         */
+        @Deprecated
+        @RequiresApi(Build.VERSION_CODES.N)
+        public Builder(@NonNull java.util.function.Predicate<Pair<Activity, Activity>>
+                        activityPairPredicate,
+                @NonNull java.util.function.Predicate<Pair<Activity, Intent>>
+                        activityIntentPredicate,
+                @NonNull java.util.function.Predicate<WindowMetrics>
+                        parentWindowMetricsPredicate) {
+            mActivityPairPredicate = activityPairPredicate::test;
+            mActivityIntentPredicate = activityIntentPredicate::test;
+            mParentWindowMetricsPredicate = parentWindowMetricsPredicate::test;
+        }
+
+        /**
+         * The {@link SplitPairRule} builder constructor
+         *
+         * @param activityPairPredicate the {@link Predicate} to verify if an {@link Activity} pair
+         *                              matches this rule
+         * @param activityIntentPredicate the {@link Predicate} to verify if an ({@link Activity},
+         *                              {@link Intent}) pair matches this rule
+         * @param parentWindowMetricsPredicate the {@link Predicate} to verify if the matched split
+         *                               pair is allowed to show adjacent to each other with the
+         *                               given parent {@link WindowMetrics}
+         * Since {@link WindowExtensions#VENDOR_API_LEVEL_2}
+         */
+        public Builder(@NonNull Predicate<Pair<Activity, Activity>> activityPairPredicate,
+                @NonNull Predicate<Pair<Activity, Intent>> activityIntentPredicate,
+                @NonNull Predicate<WindowMetrics> parentWindowMetricsPredicate) {
+            mActivityPairPredicate = activityPairPredicate;
+            mActivityIntentPredicate = activityIntentPredicate;
+            mParentWindowMetricsPredicate = parentWindowMetricsPredicate;
+        }
+
+        /**
+         * @deprecated Use {@link #setDefaultSplitAttributes(SplitAttributes)} starting with
+         * {@link WindowExtensions#VENDOR_API_LEVEL_2}. Only used if
+         * {@link #setDefaultSplitAttributes(SplitAttributes)} can't be called on
+         * {@link WindowExtensions#VENDOR_API_LEVEL_1}. {@code splitRatio} will be translated to
+         * {@link SplitAttributes.SplitType.ExpandContainersSplitType} for value {@code 0.0} and
+         * {@code 1.0}, and {@link SplitAttributes.SplitType.RatioSplitType} for value with range
+         * (0.0, 1.0).
+         */
+        @Deprecated
+        @NonNull
+        public Builder setSplitRatio(@FloatRange(from = 0.0, to = 1.0) float splitRatio) {
+            mSplitRatio = splitRatio;
+            return this;
+        }
+
+        /**
+         * @deprecated Use {@link #setDefaultSplitAttributes(SplitAttributes)} starting with
+         * {@link WindowExtensions#VENDOR_API_LEVEL_2}. Only used if
+         * {@link #setDefaultSplitAttributes(SplitAttributes)} can't be called on
+         * {@link WindowExtensions#VENDOR_API_LEVEL_1}.
+         */
+        @Deprecated
+        @NonNull
+        public Builder setLayoutDirection(@SplitAttributes.ExtLayoutDirection int layoutDirection) {
+            mLayoutDirection = layoutDirection;
+            return this;
+        }
+
+        /**
+         * See {@link SplitPairRule#getDefaultSplitAttributes()} for reference.
+         * Overrides values if set in {@link #setSplitRatio(float)} and
+         * {@link #setLayoutDirection(int)}
+         *
+         * Since {@link WindowExtensions#VENDOR_API_LEVEL_2}
+         */
+        @NonNull
+        public Builder setDefaultSplitAttributes(@NonNull SplitAttributes attrs) {
+            mDefaultSplitAttributes = attrs;
+            return this;
+        }
+
+        /** @deprecated To be removed with next developer preview. */
+        @Deprecated
+        @NonNull
+        public Builder setShouldFinishPrimaryWithSecondary(
+                boolean finishPrimaryWithSecondary) {
+            return this;
+        }
+
+        /** @deprecated To be removed with next developer preview. */
+        @Deprecated
+        @NonNull
+        public Builder setShouldFinishSecondaryWithPrimary(boolean finishSecondaryWithPrimary) {
+            return this;
+        }
+
+        /** @see SplitPairRule#getFinishPrimaryWithSecondary() */
+        @NonNull
+        public Builder setFinishPrimaryWithSecondary(@SplitFinishBehavior int finishBehavior) {
+            mFinishPrimaryWithSecondary = finishBehavior;
+            return this;
+        }
+
+        /** @see SplitPairRule#getFinishSecondaryWithPrimary() */
+        @NonNull
+        public Builder setFinishSecondaryWithPrimary(@SplitFinishBehavior int finishBehavior) {
+            mFinishSecondaryWithPrimary = finishBehavior;
+            return this;
+        }
+
+        /** @see SplitPairRule#shouldClearTop() */
+        @NonNull
+        public Builder setShouldClearTop(boolean shouldClearTop) {
+            mClearTop = shouldClearTop;
+            return this;
+        }
+
+        /**
+         * @see SplitPairRule#getTag()
+         * Since {@link WindowExtensions#VENDOR_API_LEVEL_2}
+         */
+        @NonNull
+        public Builder setTag(@NonNull String tag) {
+            mTag = Objects.requireNonNull(tag);
+            return this;
+        }
+
+        /** Builds a new instance of {@link SplitPairRule}. */
+        @NonNull
+        public SplitPairRule build() {
+            // To provide compatibility with prior version of WM Jetpack library, where
+            // #setDefaultAttributes hasn't yet been supported and thus would not be set.
+            mDefaultSplitAttributes = (mDefaultSplitAttributes != null)
+                    ? mDefaultSplitAttributes
+                    : new SplitAttributes.Builder()
+                            .setSplitType(createSplitTypeFromLegacySplitRatio(mSplitRatio))
+                            .setLayoutDirection(mLayoutDirection)
+                            .build();
+            return new SplitPairRule(mDefaultSplitAttributes,
+                    mFinishPrimaryWithSecondary, mFinishSecondaryWithPrimary,
+                    mClearTop, mActivityPairPredicate, mActivityIntentPredicate,
+                    mParentWindowMetricsPredicate, mTag);
+        }
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof SplitPairRule)) return false;
+        SplitPairRule that = (SplitPairRule) o;
+        return super.equals(o)
+                && mActivityPairPredicate.equals(that.mActivityPairPredicate)
+                && mActivityIntentPredicate.equals(that.mActivityIntentPredicate)
+                && mFinishPrimaryWithSecondary == that.mFinishPrimaryWithSecondary
+                && mFinishSecondaryWithPrimary == that.mFinishSecondaryWithPrimary
+                && mClearTop == that.mClearTop;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = super.hashCode();
+        result = 31 * result + mActivityPairPredicate.hashCode();
+        result = 31 * result + mActivityIntentPredicate.hashCode();
+        result = 31 * result + mFinishPrimaryWithSecondary;
+        result = 31 * result + mFinishSecondaryWithPrimary;
+        result = 31 * result + (mClearTop ? 1 : 0);
+        return result;
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        return "SplitPairRule{"
+                + "mTag=" + getTag()
+                + ", mDefaultSplitAttributes=" + getDefaultSplitAttributes()
+                + ", mFinishPrimaryWithSecondary=" + mFinishPrimaryWithSecondary
+                + ", mFinishSecondaryWithPrimary=" + mFinishSecondaryWithPrimary
+                + ", mClearTop=" + mClearTop
+                + '}';
+    }
+}
diff --git a/window_extensions/java/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/SplitPlaceholderRule.java b/window_extensions/java/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/SplitPlaceholderRule.java
new file mode 100644
index 0000000..38b5451
--- /dev/null
+++ b/window_extensions/java/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/SplitPlaceholderRule.java
@@ -0,0 +1,346 @@
+/*
+ * Copyright 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 androidx.window.extensions.embedding;
+
+import static androidx.window.extensions.embedding.SplitAttributes.SplitType.createSplitTypeFromLegacySplitRatio;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Build;
+import android.view.WindowMetrics;
+
+import androidx.annotation.FloatRange;
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+import androidx.window.extensions.WindowExtensions;
+import androidx.window.extensions.core.util.function.Predicate;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Split configuration rules for split placeholders - activities used to occupy additional
+ * available space on the side before the user selects content to show.
+ */
+public class SplitPlaceholderRule extends SplitRule {
+    @NonNull
+    private final Predicate<Activity> mActivityPredicate;
+    @NonNull
+    private final Predicate<Intent> mIntentPredicate;
+    @NonNull
+    private final Intent mPlaceholderIntent;
+    private final boolean mIsSticky;
+
+    /**
+     * Determines what happens with the primary container when the placeholder activity is
+     * finished in one of the containers in a split.
+     */
+    @IntDef({
+            FINISH_ALWAYS,
+            FINISH_ADJACENT
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface SplitPlaceholderFinishBehavior{}
+
+    @SplitPlaceholderFinishBehavior
+    private final int mFinishPrimaryWithPlaceholder;
+
+    SplitPlaceholderRule(@NonNull Intent placeholderIntent,
+            @NonNull SplitAttributes defaultSplitAttributes,
+            boolean isSticky,
+            @SplitPlaceholderFinishBehavior int finishPrimaryWithPlaceholder,
+            @NonNull Predicate<Activity> activityPredicate,
+            @NonNull Predicate<Intent> intentPredicate,
+            @NonNull Predicate<WindowMetrics> parentWindowMetricsPredicate,
+            @Nullable String tag) {
+        super(parentWindowMetricsPredicate, defaultSplitAttributes, tag);
+        mIsSticky = isSticky;
+        mFinishPrimaryWithPlaceholder = finishPrimaryWithPlaceholder;
+        mActivityPredicate = activityPredicate;
+        mIntentPredicate = intentPredicate;
+        mPlaceholderIntent = placeholderIntent;
+    }
+
+    /**
+     * Checks if the rule is applicable to the provided activity.
+     */
+    @SuppressLint("ClassVerificationFailure") // Only called by Extensions implementation on device.
+    @RequiresApi(api = Build.VERSION_CODES.N)
+    public boolean matchesActivity(@NonNull Activity activity) {
+        return mActivityPredicate.test(activity);
+    }
+
+    /**
+     * Checks if the rule is applicable to the provided activity intent.
+     */
+    @SuppressLint("ClassVerificationFailure") // Only called by Extensions implementation on device.
+    @RequiresApi(api = Build.VERSION_CODES.N)
+    public boolean matchesIntent(@NonNull Intent intent) {
+        return mIntentPredicate.test(intent);
+    }
+
+    /**
+     * An {@link Intent} used by Extensions Sidecar to launch the placeholder when the space allows.
+     */
+    @NonNull
+    public Intent getPlaceholderIntent() {
+        return mPlaceholderIntent;
+    }
+
+    /**
+     * Determines whether the placeholder will show on top in a smaller window size after it first
+     * appeared in a split with sufficient minimum width.
+     */
+    public boolean isSticky() {
+        return mIsSticky;
+    }
+
+    /**
+     * @deprecated Use {@link #getFinishPrimaryWithPlaceholder()} instead starting with
+     * {@link WindowExtensions#VENDOR_API_LEVEL_2}. Only used if
+     * {@link #getFinishPrimaryWithPlaceholder()} can't be called on
+     * {@link WindowExtensions#VENDOR_API_LEVEL_1}.
+     */
+    @Deprecated
+    @SplitPlaceholderFinishBehavior
+    public int getFinishPrimaryWithSecondary() {
+        return getFinishPrimaryWithPlaceholder();
+    }
+
+    /**
+     * Determines what happens with the primary container when all activities are finished in the
+     * associated secondary/placeholder container.
+     *
+     * Since {@link WindowExtensions#VENDOR_API_LEVEL_2}
+     */
+    // TODO(b/238905747): Add api guard for extensions.
+    @SplitPlaceholderFinishBehavior
+    public int getFinishPrimaryWithPlaceholder() {
+        return mFinishPrimaryWithPlaceholder;
+    }
+
+    /**
+     * Builder for {@link SplitPlaceholderRule}.
+     */
+    public static final class Builder {
+        @NonNull
+        private final Predicate<Activity> mActivityPredicate;
+        @NonNull
+        private final Predicate<Intent> mIntentPredicate;
+        @NonNull
+        private final Predicate<WindowMetrics> mParentWindowMetricsPredicate;
+        @NonNull
+        private final Intent mPlaceholderIntent;
+        // Keep for backward compatibility
+        @FloatRange(from = 0.0, to = 1.0)
+        private float mSplitRatio;
+        // Keep for backward compatibility
+        @SplitAttributes.ExtLayoutDirection
+        private int mLayoutDirection;
+        private SplitAttributes mDefaultSplitAttributes;
+        private boolean mIsSticky = false;
+        @SplitPlaceholderFinishBehavior
+        private int mFinishPrimaryWithPlaceholder = FINISH_ALWAYS;
+        @Nullable
+        private String mTag;
+
+        /**
+         * @deprecated Use {@link #Builder(Intent, Predicate, Predicate, Predicate)} starting with
+         * {@link WindowExtensions#VENDOR_API_LEVEL_2}. Only used if
+         * {@link #Builder(Intent, Predicate, Predicate, Predicate)} can't be called on
+         * {@link WindowExtensions#VENDOR_API_LEVEL_1}.
+         */
+        @Deprecated
+        @RequiresApi(Build.VERSION_CODES.N)
+        public Builder(@NonNull Intent placeholderIntent,
+                @NonNull java.util.function.Predicate<Activity> activityPredicate,
+                @NonNull java.util.function.Predicate<Intent> intentPredicate,
+                @NonNull java.util.function.Predicate<WindowMetrics> parentWindowMetricsPredicate) {
+            mActivityPredicate = activityPredicate::test;
+            mIntentPredicate = intentPredicate::test;
+            mPlaceholderIntent = placeholderIntent;
+            mParentWindowMetricsPredicate = parentWindowMetricsPredicate::test;
+        }
+
+        /**
+         * The {@link SplitPlaceholderRule} Builder constructor
+         * @param placeholderIntent the placeholder activity to launch if
+         *                         {@link SplitPlaceholderRule#checkParentMetrics(WindowMetrics)}
+         *                         is satisfied
+         * @param activityPredicate the {@link Predicate} to verify if a given {@link Activity}
+         *                         matches the rule
+         * @param intentPredicate the {@link Predicate} to verify if a given {@link Intent}
+         *                         matches the rule
+         * @param parentWindowMetricsPredicate the {@link Predicate} to verify if the placeholder
+         *                                     {@link Activity} should be launched with the given
+         *                                     {@link WindowMetrics}
+         * Since {@link WindowExtensions#VENDOR_API_LEVEL_2}
+         */
+        public Builder(@NonNull Intent placeholderIntent,
+                @NonNull Predicate<Activity> activityPredicate,
+                @NonNull Predicate<Intent> intentPredicate,
+                @NonNull Predicate<WindowMetrics> parentWindowMetricsPredicate) {
+            mActivityPredicate = activityPredicate;
+            mIntentPredicate = intentPredicate;
+            mPlaceholderIntent = placeholderIntent;
+            mParentWindowMetricsPredicate = parentWindowMetricsPredicate;
+        }
+
+        /**
+         * @deprecated Use {@link #setDefaultSplitAttributes(SplitAttributes)} starting with
+         * {@link WindowExtensions#VENDOR_API_LEVEL_2}. Only used if
+         * {@link #setDefaultSplitAttributes(SplitAttributes)} can't be called on
+         * {@link WindowExtensions#VENDOR_API_LEVEL_1}. {@code splitRatio} will be translated to
+         * @link SplitAttributes.SplitType.ExpandContainersSplitType} for value
+         * {@code 0.0} and {@code 1.0}, and {@link SplitAttributes.SplitType.RatioSplitType} for
+         * value with range (0.0, 1.0).
+         */
+        @Deprecated
+        @NonNull
+        public Builder setSplitRatio(@FloatRange(from = 0.0, to = 1.0) float splitRatio) {
+            mSplitRatio = splitRatio;
+            return this;
+        }
+
+        /**
+         * @deprecated Use {@link #setDefaultSplitAttributes(SplitAttributes)} starting with
+         * {@link WindowExtensions#VENDOR_API_LEVEL_2}. Only used if
+         * {@link #setDefaultSplitAttributes(SplitAttributes)} can't be called on
+         * {@link WindowExtensions#VENDOR_API_LEVEL_1}.
+         */
+        @Deprecated
+        @NonNull
+        public Builder setLayoutDirection(@SplitAttributes.ExtLayoutDirection int layoutDirection) {
+            mLayoutDirection = layoutDirection;
+            return this;
+        }
+
+        /**
+         * See {@link SplitPlaceholderRule#getDefaultSplitAttributes()} for reference.
+         * Overrides values if set in {@link #setSplitRatio(float)} and
+         * {@link #setLayoutDirection(int)}
+         *
+         * Since {@link WindowExtensions#VENDOR_API_LEVEL_2}
+         */
+        @NonNull
+        public Builder setDefaultSplitAttributes(@NonNull SplitAttributes attrs) {
+            mDefaultSplitAttributes = attrs;
+            return this;
+        }
+
+        /** @see SplitPlaceholderRule#isSticky() */
+        @NonNull
+        public Builder setSticky(boolean sticky) {
+            mIsSticky = sticky;
+            return this;
+        }
+
+        /**
+         * @deprecated Use SplitPlaceholderRule#setFinishPrimaryWithPlaceholder(int)} starting with
+         * {@link WindowExtensions#VENDOR_API_LEVEL_2}.
+         */
+        @Deprecated
+        @NonNull
+        public Builder setFinishPrimaryWithSecondary(
+                @SplitPlaceholderFinishBehavior int finishBehavior) {
+            if (finishBehavior == FINISH_NEVER) {
+                finishBehavior = FINISH_ALWAYS;
+            }
+            return setFinishPrimaryWithPlaceholder(finishBehavior);
+        }
+
+        /**
+         * @see SplitPlaceholderRule#getFinishPrimaryWithPlaceholder()
+         * Since {@link WindowExtensions#VENDOR_API_LEVEL_2}
+         */
+        // TODO(b/238905747): Add api guard for extensions.
+        @NonNull
+        public Builder setFinishPrimaryWithPlaceholder(
+                @SplitPlaceholderFinishBehavior int finishBehavior) {
+            mFinishPrimaryWithPlaceholder = finishBehavior;
+            return this;
+        }
+
+        /**
+         * @see SplitPlaceholderRule#getTag()
+         * Since {@link WindowExtensions#VENDOR_API_LEVEL_2}
+         */
+        @NonNull
+        public Builder setTag(@NonNull String tag) {
+            mTag = Objects.requireNonNull(tag);
+            return this;
+        }
+
+        /** Builds a new instance of {@link SplitPlaceholderRule}. */
+        @NonNull
+        public SplitPlaceholderRule build() {
+            // To provide compatibility with prior version of WM Jetpack library, where
+            // #setDefaultAttributes hasn't yet been supported and thus would not be set.
+            mDefaultSplitAttributes = (mDefaultSplitAttributes != null)
+                    ? mDefaultSplitAttributes
+                    : new SplitAttributes.Builder()
+                            .setSplitType(createSplitTypeFromLegacySplitRatio(mSplitRatio))
+                            .setLayoutDirection(mLayoutDirection)
+                            .build();
+            return new SplitPlaceholderRule(mPlaceholderIntent, mDefaultSplitAttributes, mIsSticky,
+                    mFinishPrimaryWithPlaceholder, mActivityPredicate,
+                    mIntentPredicate, mParentWindowMetricsPredicate, mTag);
+        }
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof SplitPlaceholderRule)) return false;
+        if (!super.equals(o)) return false;
+
+        SplitPlaceholderRule that = (SplitPlaceholderRule) o;
+
+        if (mIsSticky != that.mIsSticky) return false;
+        if (mFinishPrimaryWithPlaceholder != that.mFinishPrimaryWithPlaceholder) return false;
+        if (!mActivityPredicate.equals(that.mActivityPredicate)) return false;
+        if (!mIntentPredicate.equals(that.mIntentPredicate)) return false;
+        return mPlaceholderIntent.equals(that.mPlaceholderIntent);
+    }
+
+    @Override
+    public int hashCode() {
+        int result = super.hashCode();
+        result = 31 * result + mActivityPredicate.hashCode();
+        result = 31 * result + mIntentPredicate.hashCode();
+        result = 31 * result + mPlaceholderIntent.hashCode();
+        result = 31 * result + (mIsSticky ? 1 : 0);
+        result = 31 * result + mFinishPrimaryWithPlaceholder;
+        return result;
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        return "SplitPlaceholderRule{"
+                + "mTag=" + getTag()
+                + ", mDefaultSplitAttributes=" + getDefaultSplitAttributes()
+                + ", mActivityPredicate=" + mActivityPredicate
+                + ", mIsSticky=" + mIsSticky
+                + ", mFinishPrimaryWithPlaceholder=" + mFinishPrimaryWithPlaceholder
+                + '}';
+    }
+}
diff --git a/window_extensions/java/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/SplitRule.java b/window_extensions/java/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/SplitRule.java
new file mode 100644
index 0000000..dd02b14
--- /dev/null
+++ b/window_extensions/java/window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/SplitRule.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright 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 androidx.window.extensions.embedding;
+
+import android.annotation.SuppressLint;
+import android.os.Build;
+import android.view.WindowMetrics;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+import androidx.window.extensions.WindowExtensions;
+import androidx.window.extensions.core.util.function.Predicate;
+import androidx.window.extensions.embedding.SplitAttributes.SplitType;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Split configuration rules for activities that are launched to side in a split. Define when an
+ * activity that was launched in a side container from another activity should be shown
+ * adjacent or on top of it, as well as the visual properties of the split. Can be applied to
+ * new activities started from the same process automatically by the embedding implementation on
+ * the device.
+ */
+public abstract class SplitRule extends EmbeddingRule {
+    @NonNull
+    private final Predicate<WindowMetrics> mParentWindowMetricsPredicate;
+
+    @NonNull
+    private final SplitAttributes mDefaultSplitAttributes;
+
+    /**
+     * Never finish the associated container.
+     * @see SplitFinishBehavior
+     */
+    public static final int FINISH_NEVER = 0;
+    /**
+     * Always finish the associated container independent of the current presentation mode.
+     * @see SplitFinishBehavior
+     */
+    public static final int FINISH_ALWAYS = 1;
+    /**
+     * Only finish the associated container when displayed adjacent to the one being finished. Does
+     * not finish the associated one when containers are stacked on top of each other.
+     * @see SplitFinishBehavior
+     */
+    public static final int FINISH_ADJACENT = 2;
+
+    /**
+     * Determines what happens with the associated container when all activities are finished in
+     * one of the containers in a split.
+     * <p>
+     * For example, given that {@link SplitPairRule#getFinishPrimaryWithSecondary()} is
+     * {@link #FINISH_ADJACENT} and secondary container finishes. The primary associated
+     * container is finished if it's shown adjacent to the secondary container. The primary
+     * associated container is not finished if it occupies entire task bounds.</p>
+     *
+     * @see SplitPairRule#getFinishPrimaryWithSecondary()
+     * @see SplitPairRule#getFinishSecondaryWithPrimary()
+     * @see SplitPlaceholderRule#getFinishPrimaryWithSecondary()
+     */
+    @IntDef({
+            FINISH_NEVER,
+            FINISH_ALWAYS,
+            FINISH_ADJACENT
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface SplitFinishBehavior {}
+
+    SplitRule(@NonNull Predicate<WindowMetrics> parentWindowMetricsPredicate,
+            @NonNull SplitAttributes defaultSplitAttributes, @Nullable String tag) {
+        super(tag);
+        mParentWindowMetricsPredicate = parentWindowMetricsPredicate;
+        mDefaultSplitAttributes = defaultSplitAttributes;
+    }
+
+    /**
+     * Checks whether the parent window satisfied the dimensions and aspect ratios requirements
+     * specified in the {@link androidx.window.embedding.SplitRule}, which are
+     * {@link androidx.window.embedding.SplitRule#minWidthDp},
+     * {@link androidx.window.embedding.SplitRule#minHeightDp},
+     * {@link androidx.window.embedding.SplitRule#minSmallestWidthDp},
+     * {@link androidx.window.embedding.SplitRule#maxAspectRatioInPortrait} and
+     * {@link androidx.window.embedding.SplitRule#maxAspectRatioInLandscape}.
+     *
+     * @param parentMetrics the {@link WindowMetrics} of the parent window.
+     * @return whether the parent window satisfied the {@link SplitRule} requirements.
+     */
+    @SuppressLint("ClassVerificationFailure") // Only called by Extensions implementation on device.
+    @RequiresApi(api = Build.VERSION_CODES.N)
+    public boolean checkParentMetrics(@NonNull WindowMetrics parentMetrics) {
+        return mParentWindowMetricsPredicate.test(parentMetrics);
+    }
+
+    /**
+     * @deprecated Use {@link #getDefaultSplitAttributes()} instead starting with
+     * {@link WindowExtensions#VENDOR_API_LEVEL_2}. Only used if
+     * {@link #getDefaultSplitAttributes()} can't be called on
+     * {@link WindowExtensions#VENDOR_API_LEVEL_1}.
+     */
+    @Deprecated
+    public float getSplitRatio() {
+        final SplitType splitType = mDefaultSplitAttributes.getSplitType();
+        if (splitType instanceof SplitType.RatioSplitType) {
+            return ((SplitType.RatioSplitType) splitType).getRatio();
+        } else { // Fallback to use 0.0 because the WM Jetpack may not support HingeSplitType.
+            return 0.0f;
+        }
+    }
+
+    /**
+     * @deprecated Use {@link #getDefaultSplitAttributes()} instead starting with
+     * {@link WindowExtensions#VENDOR_API_LEVEL_2}. Only used if
+     * {@link #getDefaultSplitAttributes()} can't be called on
+     * {@link WindowExtensions#VENDOR_API_LEVEL_1}.
+     */
+    @Deprecated
+    @SplitAttributes.ExtLayoutDirection
+    public int getLayoutDirection() {
+        return mDefaultSplitAttributes.getLayoutDirection();
+    }
+
+    /**
+     * Returns the default {@link SplitAttributes} which is applied if
+     * {@link #checkParentMetrics(WindowMetrics)} is {@code true}.
+     *
+     * Since {@link WindowExtensions#VENDOR_API_LEVEL_2}
+     */
+    @NonNull
+    public SplitAttributes getDefaultSplitAttributes() {
+        return mDefaultSplitAttributes;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof SplitRule)) return false;
+        SplitRule that = (SplitRule) o;
+        return super.equals(that)
+                && mDefaultSplitAttributes.equals(that.mDefaultSplitAttributes)
+                && mParentWindowMetricsPredicate.equals(that.mParentWindowMetricsPredicate);
+    }
+
+    @Override
+    public int hashCode() {
+        int result = super.hashCode();
+        result = 31 * result + mParentWindowMetricsPredicate.hashCode();
+        result = 31 * result + Objects.hashCode(mDefaultSplitAttributes);
+        return result;
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        return "SplitRule{"
+                + "mTag=" + getTag()
+                + ", mDefaultSplitAttributes=" + mDefaultSplitAttributes
+                + '}';
+    }
+}
diff --git a/window_extensions/java/window/extensions/extensions/src/main/java/androidx/window/extensions/layout/DisplayFeature.java b/window_extensions/java/window/extensions/extensions/src/main/java/androidx/window/extensions/layout/DisplayFeature.java
new file mode 100644
index 0000000..32b0fa1
--- /dev/null
+++ b/window_extensions/java/window/extensions/extensions/src/main/java/androidx/window/extensions/layout/DisplayFeature.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 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 androidx.window.extensions.layout;
+
+import android.graphics.Rect;
+
+import androidx.annotation.NonNull;
+
+/**
+ * Description of a physical feature on the display.
+ */
+public interface DisplayFeature {
+
+    /**
+     * The bounding rectangle of the feature within the application window
+     * in the window coordinate space.
+     *
+     * @return bounds of display feature.
+     */
+    @NonNull
+    Rect getBounds();
+}
diff --git a/window_extensions/java/window/extensions/extensions/src/main/java/androidx/window/extensions/layout/FoldingFeature.java b/window_extensions/java/window/extensions/extensions/src/main/java/androidx/window/extensions/layout/FoldingFeature.java
new file mode 100644
index 0000000..9f95510
--- /dev/null
+++ b/window_extensions/java/window/extensions/extensions/src/main/java/androidx/window/extensions/layout/FoldingFeature.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright 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 androidx.window.extensions.layout;
+
+import android.graphics.Rect;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * A feature that describes a fold in a flexible display
+ * or a hinge between two physical display panels.
+ */
+public class FoldingFeature implements DisplayFeature {
+
+    /**
+     * A fold in the flexible screen without a physical gap.
+     */
+    public static final int TYPE_FOLD = 1;
+
+    /**
+     * A physical separation with a hinge that allows two display panels to fold.
+     */
+    public static final int TYPE_HINGE = 2;
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({
+            TYPE_FOLD,
+            TYPE_HINGE,
+    })
+    @interface Type{}
+
+    /**
+     * The foldable device's hinge is completely open, the screen space that is presented to the
+     * user is flat. See the
+     * <a href="https://developer.android.com/guide/topics/ui/foldables#postures">Posture</a>
+     * section in the official documentation for visual samples and references.
+     */
+    public static final int STATE_FLAT = 1;
+
+    /**
+     * The foldable device's hinge is in an intermediate position between opened and closed state,
+     * there is a non-flat angle between parts of the flexible screen or between physical screen
+     * panels. See the
+     * <a href="https://developer.android.com/guide/topics/ui/foldables#postures">Posture</a>
+     * section in the official documentation for visual samples and references.
+     */
+    public static final int STATE_HALF_OPENED = 2;
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({
+            STATE_HALF_OPENED,
+            STATE_FLAT
+    })
+    @interface State {}
+
+    /**
+     * The bounding rectangle of the feature within the application window in the window
+     * coordinate space.
+     */
+    @NonNull
+    private final Rect mBounds;
+
+    /**
+     * The physical type of the feature.
+     */
+    @Type
+    private final int mType;
+
+    /**
+     * The state of the feature.
+     */
+    @State
+    private final int mState;
+
+    public FoldingFeature(@NonNull Rect bounds, @Type int type, @State int state) {
+        validateFeatureBounds(bounds);
+        mBounds = new Rect(bounds);
+        mType = type;
+        mState = state;
+    }
+
+    /** Gets the bounding rect of the display feature in window coordinate space. */
+    @NonNull
+    @Override
+    public Rect getBounds() {
+        return new Rect(mBounds);
+    }
+
+    /** Gets the type of the display feature. */
+    @Type
+    public int getType() {
+        return mType;
+    }
+
+    /** Gets the state of the display feature. */
+    @State
+    public int getState() {
+        return mState;
+    }
+
+    /**
+     * Verifies the bounds of the folding feature.
+     */
+    private static void validateFeatureBounds(@NonNull Rect bounds) {
+        if (bounds.width() == 0 && bounds.height() == 0) {
+            throw new IllegalArgumentException("Bounds must be non zero.  Bounds: " + bounds);
+        }
+        if (bounds.left != 0 && bounds.top != 0) {
+            throw new IllegalArgumentException("Bounding rectangle must start at the top or "
+                    + "left window edge for folding features.  Bounds: " + bounds);
+        }
+    }
+
+    @NonNull
+    private static String typeToString(int type) {
+        switch (type) {
+            case TYPE_FOLD:
+                return "FOLD";
+            case TYPE_HINGE:
+                return "HINGE";
+            default:
+                return "Unknown feature type (" + type + ")";
+        }
+    }
+
+    @NonNull
+    private static String stateToString(int state) {
+        switch (state) {
+            case STATE_FLAT:
+                return "FLAT";
+            case STATE_HALF_OPENED:
+                return "HALF_OPENED";
+            default:
+                return "Unknown feature state (" + state + ")";
+        }
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        return "ExtensionDisplayFoldFeature { " + mBounds
+                + ", type=" + typeToString(getType()) + ", state=" + stateToString(mState) + " }";
+    }
+
+    @Override
+    public boolean equals(@Nullable Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof FoldingFeature)) {
+            return false;
+        }
+        final FoldingFeature other = (FoldingFeature) obj;
+        if (mType != other.mType) {
+            return false;
+        }
+        if (mState != other.mState) {
+            return false;
+        }
+        return mBounds.equals(other.mBounds);
+    }
+
+    /**
+     * Manually computes the hashCode for the {@link Rect} since it is not implemented
+     * on API 15
+     */
+    private static int hashBounds(Rect bounds) {
+        int result = bounds.left;
+        result = 31 * result + bounds.top;
+        result = 31 * result + bounds.right;
+        result = 31 * result + bounds.bottom;
+        return result;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = hashBounds(mBounds);
+        result = 31 * result + mType;
+        result = 31 * result + mState;
+        return result;
+    }
+}
diff --git a/window_extensions/java/window/extensions/extensions/src/main/java/androidx/window/extensions/layout/WindowLayoutComponent.java b/window_extensions/java/window/extensions/extensions/src/main/java/androidx/window/extensions/layout/WindowLayoutComponent.java
new file mode 100644
index 0000000..0b7c01f
--- /dev/null
+++ b/window_extensions/java/window/extensions/extensions/src/main/java/androidx/window/extensions/layout/WindowLayoutComponent.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 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 androidx.window.extensions.layout;
+
+import android.app.Activity;
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.UiContext;
+import androidx.window.extensions.WindowExtensions;
+import androidx.window.extensions.core.util.function.Consumer;
+
+/**
+ * The interface definition that will be used by the WindowManager library to get custom
+ * OEM-provided information about the window that isn't covered by platform APIs. Exposes methods
+ * to listen to changes in the {@link WindowLayoutInfo}. A {@link WindowLayoutInfo} contains a list
+ * of {@link DisplayFeature}s.
+ * <p>
+ * Currently {@link FoldingFeature} is the only {@link DisplayFeature}. A {@link FoldingFeature}
+ * exposes the state of a hinge and the relative bounds within the window. Developers can
+ * optimize their UI to support a {@link FoldingFeature} by avoiding it and placing content in
+ * relevant logical areas.
+ *
+ * <p>This interface should be implemented by OEM and deployed to the target devices.
+ * @see WindowExtensions#getWindowLayoutComponent()
+ */
+public interface WindowLayoutComponent {
+    /**
+     * @deprecated Use {@link #addWindowLayoutInfoListener(Context, Consumer)}
+     * starting with {@link WindowExtensions#VENDOR_API_LEVEL_2}. Only used if
+     * {@link #addWindowLayoutInfoListener(Context, Consumer)} can't be
+     * called on {@link WindowExtensions#VENDOR_API_LEVEL_1}.
+     * Since {@link WindowExtensions#VENDOR_API_LEVEL_1}
+     */
+    @Deprecated
+    void addWindowLayoutInfoListener(@NonNull Activity activity,
+            @NonNull java.util.function.Consumer<WindowLayoutInfo> consumer);
+
+    /**
+     * @deprecated Use {@link #removeWindowLayoutInfoListener(Consumer)} starting with
+     * {@link WindowExtensions#VENDOR_API_LEVEL_2}. Only used if
+     * {@link #removeWindowLayoutInfoListener(Consumer)} can't be called on
+     * {@link WindowExtensions#VENDOR_API_LEVEL_1}.
+     * Since {@link WindowExtensions#VENDOR_API_LEVEL_1}
+     */
+    @Deprecated
+    void removeWindowLayoutInfoListener(
+            @NonNull java.util.function.Consumer<WindowLayoutInfo> consumer);
+
+    /**
+     * Adds a listener interested in receiving updates to {@link WindowLayoutInfo}.
+     * Use {@link WindowLayoutComponent#removeWindowLayoutInfoListener} to remove listener.
+     * <p>
+     * A {@link Context} or a Consumer instance can only be registered once.
+     * Registering the same {@link Context} or Consumer more than once will result in
+     * a noop.
+     *
+     * @param context a {@link UiContext} that corresponds to a window or an area on the
+     *                      screen - an {@link Activity}, a {@link Context} created with
+     *                      {@link Context#createWindowContext(Display, int , Bundle)}, or
+     *                      {@link android.inputmethodservice.InputMethodService}.
+     * @param consumer interested in receiving updates to {@link WindowLayoutInfo}
+     * Since {@link WindowExtensions#VENDOR_API_LEVEL_2}
+     */
+    // TODO(b/238905747): Add api guard for extensions.
+    @SuppressWarnings("PairedRegistration")
+    // The paired method for unregistering is also removeWindowLayoutInfoListener.
+    default void addWindowLayoutInfoListener(@NonNull @UiContext Context context,
+            @NonNull Consumer<WindowLayoutInfo> consumer) {
+        throw new UnsupportedOperationException("This method must not be called unless there is a"
+                + " corresponding override implementation on the device.");
+    }
+
+    /**
+     * Removes a listener no longer interested in receiving updates.
+     *
+     * @param consumer no longer interested in receiving updates to {@link WindowLayoutInfo}
+     * Since {@link WindowExtensions#VENDOR_API_LEVEL_2}
+     */
+    default void removeWindowLayoutInfoListener(@NonNull Consumer<WindowLayoutInfo> consumer) {
+        throw new UnsupportedOperationException("This method must not be called unless there is a"
+                + " corresponding override implementation on the device.");
+    }
+}
diff --git a/window_extensions/java/window/extensions/extensions/src/main/java/androidx/window/extensions/layout/WindowLayoutInfo.java b/window_extensions/java/window/extensions/extensions/src/main/java/androidx/window/extensions/layout/WindowLayoutInfo.java
new file mode 100644
index 0000000..9041f0a
--- /dev/null
+++ b/window_extensions/java/window/extensions/extensions/src/main/java/androidx/window/extensions/layout/WindowLayoutInfo.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 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 androidx.window.extensions.layout;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Contains information about the layout of display features within the window.
+ */
+public class WindowLayoutInfo {
+
+    /**
+     * List of display features within the window.
+     * <p>NOTE: All display features returned with this container must be cropped to the application
+     * window and reported within the coordinate space of the window that was provided by the app.
+     */
+    @NonNull
+    private List<DisplayFeature> mDisplayFeatures;
+
+    public WindowLayoutInfo(@NonNull List<DisplayFeature> displayFeatures) {
+        mDisplayFeatures = Collections.unmodifiableList(displayFeatures);
+    }
+
+    /**
+     * Gets the list of display features present within the window.
+     */
+    @NonNull
+    public List<DisplayFeature> getDisplayFeatures() {
+        return mDisplayFeatures;
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("ExtensionWindowLayoutInfo { ExtensionDisplayFeatures[ ");
+        for (int i = 0; i < mDisplayFeatures.size(); i = i + 1) {
+            sb.append(mDisplayFeatures.get(i));
+            if (i < mDisplayFeatures.size() - 1) {
+                sb.append(", ");
+            }
+        }
+        sb.append(" ] }");
+        return sb.toString();
+    }
+
+    @Override
+    public boolean equals(@Nullable Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof WindowLayoutInfo)) {
+            return false;
+        }
+        final WindowLayoutInfo
+                other = (WindowLayoutInfo) obj;
+        if (mDisplayFeatures == null) {
+            return other.mDisplayFeatures == null;
+        }
+        return mDisplayFeatures.equals(other.mDisplayFeatures);
+    }
+
+    @Override
+    public int hashCode() {
+        return mDisplayFeatures != null ? mDisplayFeatures.size() : 0;
+    }
+}
diff --git a/window_extensions/sources.txt b/window_extensions/sources.txt
new file mode 100644
index 0000000..2ccdd8c
--- /dev/null
+++ b/window_extensions/sources.txt
@@ -0,0 +1,22 @@
+window/extensions/core/core/src/main/java/androidx/window/extensions/core/util/function/Consumer.java
+window/extensions/core/core/src/main/java/androidx/window/extensions/core/util/function/Function.java
+window/extensions/core/core/src/main/java/androidx/window/extensions/core/util/function/Predicate.java
+window/extensions/extensions/src/main/java/androidx/window/extensions/area/ExtensionWindowAreaPresentation.java
+window/extensions/extensions/src/main/java/androidx/window/extensions/area/ExtensionWindowAreaStatus.java
+window/extensions/extensions/src/main/java/androidx/window/extensions/area/WindowAreaComponent.java
+window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/ActivityEmbeddingComponent.java
+window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/ActivityRule.java
+window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/ActivityStack.java
+window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/EmbeddingRule.java
+window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/SplitAttributesCalculatorParams.java
+window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/SplitAttributes.java
+window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/SplitInfo.java
+window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/SplitPairRule.java
+window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/SplitPlaceholderRule.java
+window/extensions/extensions/src/main/java/androidx/window/extensions/embedding/SplitRule.java
+window/extensions/extensions/src/main/java/androidx/window/extensions/layout/DisplayFeature.java
+window/extensions/extensions/src/main/java/androidx/window/extensions/layout/FoldingFeature.java
+window/extensions/extensions/src/main/java/androidx/window/extensions/layout/WindowLayoutComponent.java
+window/extensions/extensions/src/main/java/androidx/window/extensions/layout/WindowLayoutInfo.java
+window/extensions/extensions/src/main/java/androidx/window/extensions/WindowExtensions.java
+window/extensions/extensions/src/main/java/androidx/window/extensions/WindowExtensionsProvider.java
diff --git a/window_extensions/update_source.sh b/window_extensions/update_source.sh
new file mode 100755
index 0000000..56708cb
--- /dev/null
+++ b/window_extensions/update_source.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+
+set -e
+cd $(dirname $0)
+
+# Clone androidx repo.
+rm -rf temp-git-clone
+git clone --depth 1 https://android.googlesource.com/platform/frameworks/support temp-git-clone
+
+# Update sources.txt
+cd temp-git-clone
+find window/extensions -path "*/main/*.java" | sort > ../sources.txt
+cd ..
+
+# Copy the .java files.
+rm -r java
+for path in $(cat sources.txt); do
+  mkdir -p java/$(dirname "$path")
+  cp "temp-git-clone/$path" "java/$path"
+done
+
+rm -rf temp-git-clone
+
+echo "Job's Done."