[go: nahoru, domu]

Merge "[WebView Support Library] Expose force dark API." into androidx-master-dev
diff --git a/.idea/misc.xml b/.idea/misc.xml
index f4c5c4f..fbe0221 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -15,7 +15,7 @@
     <option name="myDefaultNotNull" value="androidx.annotation.NonNull" />
     <option name="myNullables">
       <value>
-        <list size="10">
+        <list size="12">
           <item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" />
           <item index="1" class="java.lang.String" itemvalue="javax.annotation.Nullable" />
           <item index="2" class="java.lang.String" itemvalue="javax.annotation.CheckForNull" />
@@ -26,12 +26,14 @@
           <item index="7" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.qual.Nullable" />
           <item index="8" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NullableDecl" />
           <item index="9" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NullableType" />
+          <item index="10" class="java.lang.String" itemvalue="android.annotation.Nullable" />
+          <item index="11" class="java.lang.String" itemvalue="com.android.annotations.Nullable" />
         </list>
       </value>
     </option>
     <option name="myNotNulls">
       <value>
-        <list size="9">
+        <list size="11">
           <item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" />
           <item index="1" class="java.lang.String" itemvalue="javax.annotation.Nonnull" />
           <item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" />
@@ -41,14 +43,16 @@
           <item index="6" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.qual.NonNull" />
           <item index="7" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NonNullDecl" />
           <item index="8" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NonNullType" />
+          <item index="9" class="java.lang.String" itemvalue="android.annotation.NonNull" />
+          <item index="10" class="java.lang.String" itemvalue="com.android.annotations.NonNull" />
         </list>
       </value>
     </option>
   </component>
-  <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK">
+  <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="JDK" project-jdk-type="JavaSDK">
     <output url="file://$PROJECT_DIR$/build/classes" />
   </component>
   <component name="ProjectType">
     <option name="id" value="Android" />
   </component>
-</project>
+</project>
\ No newline at end of file
diff --git a/annotation/annotation-experimental/api/1.0.0-beta01.txt b/annotation/annotation-experimental/api/1.0.0-beta01.txt
new file mode 100644
index 0000000..fce9faf2
--- /dev/null
+++ b/annotation/annotation-experimental/api/1.0.0-beta01.txt
@@ -0,0 +1,18 @@
+// Signature format: 3.0
+package androidx.annotation.experimental {
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.ANNOTATION_TYPE}) public @interface Experimental {
+    method public abstract androidx.annotation.experimental.Experimental.Level level() default androidx.annotation.experimental.Experimental.Level.ERROR;
+  }
+
+  public enum Experimental.Level {
+    enum_constant public static final androidx.annotation.experimental.Experimental.Level ERROR;
+    enum_constant public static final androidx.annotation.experimental.Experimental.Level WARNING;
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.PACKAGE}) public @interface UseExperimental {
+    method public abstract Class<?> markerClass();
+  }
+
+}
+
diff --git a/annotation/annotation-experimental/api/public_plus_experimental_1.0.0-beta01.txt b/annotation/annotation-experimental/api/public_plus_experimental_1.0.0-beta01.txt
new file mode 100644
index 0000000..fce9faf2
--- /dev/null
+++ b/annotation/annotation-experimental/api/public_plus_experimental_1.0.0-beta01.txt
@@ -0,0 +1,18 @@
+// Signature format: 3.0
+package androidx.annotation.experimental {
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.ANNOTATION_TYPE}) public @interface Experimental {
+    method public abstract androidx.annotation.experimental.Experimental.Level level() default androidx.annotation.experimental.Experimental.Level.ERROR;
+  }
+
+  public enum Experimental.Level {
+    enum_constant public static final androidx.annotation.experimental.Experimental.Level ERROR;
+    enum_constant public static final androidx.annotation.experimental.Experimental.Level WARNING;
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.PACKAGE}) public @interface UseExperimental {
+    method public abstract Class<?> markerClass();
+  }
+
+}
+
diff --git a/annotation/annotation-experimental/api/res-1.0.0-beta01.txt b/annotation/annotation-experimental/api/res-1.0.0-beta01.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/annotation/annotation-experimental/api/res-1.0.0-beta01.txt
diff --git a/annotation/annotation-experimental/api/restricted_1.0.0-beta01.txt b/annotation/annotation-experimental/api/restricted_1.0.0-beta01.txt
new file mode 100644
index 0000000..fce9faf2
--- /dev/null
+++ b/annotation/annotation-experimental/api/restricted_1.0.0-beta01.txt
@@ -0,0 +1,18 @@
+// Signature format: 3.0
+package androidx.annotation.experimental {
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.ANNOTATION_TYPE}) public @interface Experimental {
+    method public abstract androidx.annotation.experimental.Experimental.Level level() default androidx.annotation.experimental.Experimental.Level.ERROR;
+  }
+
+  public enum Experimental.Level {
+    enum_constant public static final androidx.annotation.experimental.Experimental.Level ERROR;
+    enum_constant public static final androidx.annotation.experimental.Experimental.Level WARNING;
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.PACKAGE}) public @interface UseExperimental {
+    method public abstract Class<?> markerClass();
+  }
+
+}
+
diff --git a/appcompat/src/main/java/androidx/appcompat/widget/ThemeUtils.java b/appcompat/src/main/java/androidx/appcompat/widget/ThemeUtils.java
index d114519..831a8bc 100644
--- a/appcompat/src/main/java/androidx/appcompat/widget/ThemeUtils.java
+++ b/appcompat/src/main/java/androidx/appcompat/widget/ThemeUtils.java
@@ -36,6 +36,7 @@
  */
 @RestrictTo(LIBRARY)
 public class ThemeUtils {
+    private static final String TAG = "ThemeUtils";
 
     private static final ThreadLocal<TypedValue> TL_TYPED_VALUE = new ThreadLocal<>();
 
@@ -162,9 +163,10 @@
 
         try {
             // Same check as in AppCompatDelegateImpl - do not allow using AppCompat widgets
-            // without a top-level AppCompat theme (or its descendant).
+            // without a top-level AppCompat theme (or its descendant). For now flag this as
+            // an error-level log message.
             if (!a.hasValue(R.styleable.AppCompatTheme_windowActionBar)) {
-                throw new IllegalStateException("View " + view.getClass()
+                android.util.Log.e(TAG, "View " + view.getClass()
                         + " is an AppCompat widget that can only be used with a "
                         + "Theme.AppCompat theme (or descendant).");
             }
diff --git a/benchmark/common/api/1.0.0-beta01.txt b/benchmark/common/api/1.0.0-beta01.txt
new file mode 100644
index 0000000..d811783
--- /dev/null
+++ b/benchmark/common/api/1.0.0-beta01.txt
@@ -0,0 +1,19 @@
+// Signature format: 3.0
+package androidx.benchmark {
+
+  public final class ArgumentsKt {
+    ctor public ArgumentsKt();
+  }
+
+  public final class BenchmarkState {
+    method public boolean keepRunning();
+    method public void pauseTiming();
+    method public void resumeTiming();
+    field public static final androidx.benchmark.BenchmarkState.Companion! Companion;
+  }
+
+  public static final class BenchmarkState.Companion {
+  }
+
+}
+
diff --git a/benchmark/common/api/public_plus_experimental_1.0.0-beta01.txt b/benchmark/common/api/public_plus_experimental_1.0.0-beta01.txt
new file mode 100644
index 0000000..efa9c80
--- /dev/null
+++ b/benchmark/common/api/public_plus_experimental_1.0.0-beta01.txt
@@ -0,0 +1,24 @@
+// Signature format: 3.0
+package androidx.benchmark {
+
+  public final class ArgumentsKt {
+    ctor public ArgumentsKt();
+  }
+
+  public final class BenchmarkState {
+    method public boolean keepRunning();
+    method public void pauseTiming();
+    method @androidx.benchmark.BenchmarkState.Companion.ExperimentalExternalReport public static void reportData(String className, String testName, long totalRunTimeNs, java.util.List<java.lang.Long> dataNs, @IntRange(from=0) int warmupIterations, @IntRange(from=0) long thermalThrottleSleepSeconds, @IntRange(from=1) int repeatIterations);
+    method public void resumeTiming();
+    field public static final androidx.benchmark.BenchmarkState.Companion! Companion;
+  }
+
+  public static final class BenchmarkState.Companion {
+    method @androidx.benchmark.BenchmarkState.Companion.ExperimentalExternalReport public void reportData(String className, String testName, long totalRunTimeNs, java.util.List<java.lang.Long> dataNs, @IntRange(from=0) int warmupIterations, @IntRange(from=0) long thermalThrottleSleepSeconds, @IntRange(from=1) int repeatIterations);
+  }
+
+  @kotlin.annotation.Retention(AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets=AnnotationTarget.FUNCTION) public static @interface BenchmarkState.Companion.ExperimentalExternalReport {
+  }
+
+}
+
diff --git a/benchmark/common/api/res-1.0.0-beta01.txt b/benchmark/common/api/res-1.0.0-beta01.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/benchmark/common/api/res-1.0.0-beta01.txt
diff --git a/benchmark/common/api/restricted_1.0.0-beta01.txt b/benchmark/common/api/restricted_1.0.0-beta01.txt
new file mode 100644
index 0000000..1ff8212
--- /dev/null
+++ b/benchmark/common/api/restricted_1.0.0-beta01.txt
@@ -0,0 +1,35 @@
+// Signature format: 3.0
+package androidx.benchmark {
+
+  public final class ArgumentsKt {
+    ctor public ArgumentsKt();
+  }
+
+  public final class BenchmarkState {
+    ctor @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public BenchmarkState();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public long getMin();
+    method public boolean keepRunning();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public inline boolean keepRunningInline();
+    method public void pauseTiming();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public void report(String fullClassName, String simpleClassName, String methodName);
+    method public void resumeTiming();
+    field public static final androidx.benchmark.BenchmarkState.Companion! Companion;
+  }
+
+  public static final class BenchmarkState.Companion {
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public final class IsolationActivity extends android.app.Activity {
+    method public void actuallyFinish();
+    field public static final androidx.benchmark.IsolationActivity.Companion! Companion;
+  }
+
+  public static final class IsolationActivity.Companion {
+    method @AnyThread public void finishSingleton();
+    method public boolean getResumed();
+    method @WorkerThread public void launchSingleton();
+    property public final boolean resumed;
+  }
+
+}
+
diff --git a/benchmark/junit4/api/1.0.0-beta01.txt b/benchmark/junit4/api/1.0.0-beta01.txt
new file mode 100644
index 0000000..756b010
--- /dev/null
+++ b/benchmark/junit4/api/1.0.0-beta01.txt
@@ -0,0 +1,24 @@
+// Signature format: 3.0
+package androidx.benchmark.junit4 {
+
+  public class AndroidBenchmarkRunner extends androidx.test.runner.AndroidJUnitRunner {
+    ctor public AndroidBenchmarkRunner();
+  }
+
+  public final class BenchmarkRule implements org.junit.rules.TestRule {
+    ctor public BenchmarkRule();
+    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+    method public androidx.benchmark.BenchmarkState getState();
+  }
+
+  public final class BenchmarkRule.Scope {
+    method public inline <T> T! runWithTimingDisabled(kotlin.jvm.functions.Function0<? extends T> block);
+  }
+
+  public final class BenchmarkRuleKt {
+    ctor public BenchmarkRuleKt();
+    method public static inline void measureRepeated(androidx.benchmark.junit4.BenchmarkRule, kotlin.jvm.functions.Function1<? super androidx.benchmark.junit4.BenchmarkRule.Scope,kotlin.Unit> block);
+  }
+
+}
+
diff --git a/benchmark/junit4/api/public_plus_experimental_1.0.0-beta01.txt b/benchmark/junit4/api/public_plus_experimental_1.0.0-beta01.txt
new file mode 100644
index 0000000..756b010
--- /dev/null
+++ b/benchmark/junit4/api/public_plus_experimental_1.0.0-beta01.txt
@@ -0,0 +1,24 @@
+// Signature format: 3.0
+package androidx.benchmark.junit4 {
+
+  public class AndroidBenchmarkRunner extends androidx.test.runner.AndroidJUnitRunner {
+    ctor public AndroidBenchmarkRunner();
+  }
+
+  public final class BenchmarkRule implements org.junit.rules.TestRule {
+    ctor public BenchmarkRule();
+    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+    method public androidx.benchmark.BenchmarkState getState();
+  }
+
+  public final class BenchmarkRule.Scope {
+    method public inline <T> T! runWithTimingDisabled(kotlin.jvm.functions.Function0<? extends T> block);
+  }
+
+  public final class BenchmarkRuleKt {
+    ctor public BenchmarkRuleKt();
+    method public static inline void measureRepeated(androidx.benchmark.junit4.BenchmarkRule, kotlin.jvm.functions.Function1<? super androidx.benchmark.junit4.BenchmarkRule.Scope,kotlin.Unit> block);
+  }
+
+}
+
diff --git a/benchmark/junit4/api/res-1.0.0-beta01.txt b/benchmark/junit4/api/res-1.0.0-beta01.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/benchmark/junit4/api/res-1.0.0-beta01.txt
diff --git a/benchmark/junit4/api/restricted_1.0.0-beta01.txt b/benchmark/junit4/api/restricted_1.0.0-beta01.txt
new file mode 100644
index 0000000..756b010
--- /dev/null
+++ b/benchmark/junit4/api/restricted_1.0.0-beta01.txt
@@ -0,0 +1,24 @@
+// Signature format: 3.0
+package androidx.benchmark.junit4 {
+
+  public class AndroidBenchmarkRunner extends androidx.test.runner.AndroidJUnitRunner {
+    ctor public AndroidBenchmarkRunner();
+  }
+
+  public final class BenchmarkRule implements org.junit.rules.TestRule {
+    ctor public BenchmarkRule();
+    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+    method public androidx.benchmark.BenchmarkState getState();
+  }
+
+  public final class BenchmarkRule.Scope {
+    method public inline <T> T! runWithTimingDisabled(kotlin.jvm.functions.Function0<? extends T> block);
+  }
+
+  public final class BenchmarkRuleKt {
+    ctor public BenchmarkRuleKt();
+    method public static inline void measureRepeated(androidx.benchmark.junit4.BenchmarkRule, kotlin.jvm.functions.Function1<? super androidx.benchmark.junit4.BenchmarkRule.Scope,kotlin.Unit> block);
+  }
+
+}
+
diff --git a/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt b/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
index ca0fe3d..e69bfab 100644
--- a/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
@@ -25,14 +25,14 @@
     val ANIMATION = Version("1.0.0-alpha01")
     val ANIMATION_TESTING = Version("1.1.0-alpha01")
     val ANNOTATION = Version("1.2.0-alpha01")
-    val ANNOTATION_EXPERIMENTAL = Version("1.0.0-alpha01")
+    val ANNOTATION_EXPERIMENTAL = Version("1.0.0-beta01")
     val APPCOMPAT = Version("1.2.0-alpha01")
     val ARCH_CORE = Version("2.2.0-alpha01")
     val ARCH_CORE_TESTING = ARCH_CORE
     val ARCH_RUNTIME = Version("2.2.0-alpha01")
     val ASYNCLAYOUTINFLATER = Version("1.1.0-alpha01")
     val AUTOFILL = Version("1.0.0-beta01")
-    val BENCHMARK = Version("1.0.0-alpha06")
+    val BENCHMARK = Version("1.0.0-beta01")
     val BIOMETRIC = Version("1.0.0-beta02")
     val BROWSER = Version("1.2.0-alpha08")
     val CAMERA = Version("1.0.0-alpha06")
diff --git a/buildSrc/src/main/kotlin/androidx/build/PublishDocsRules.kt b/buildSrc/src/main/kotlin/androidx/build/PublishDocsRules.kt
index 0870381..134c7f1 100644
--- a/buildSrc/src/main/kotlin/androidx/build/PublishDocsRules.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/PublishDocsRules.kt
@@ -53,7 +53,7 @@
             .addStubs("car/stubs/android.car.jar")
     prebuilts(LibraryGroups.CARDVIEW, "1.0.0")
     prebuilts(LibraryGroups.COLLECTION, "1.1.0")
-    prebuilts(LibraryGroups.CONCURRENT, "1.0.0-rc01")
+    prebuilts(LibraryGroups.CONCURRENT, "1.0.0")
     prebuilts(LibraryGroups.CONTENTPAGER, "1.0.0")
     prebuilts(LibraryGroups.COORDINATORLAYOUT, "1.1.0-beta01")
     prebuilts(LibraryGroups.CORE, "core", "1.2.0-alpha04")
diff --git a/buildSrc/src/main/kotlin/androidx/build/metalava/MetalavaRunner.kt b/buildSrc/src/main/kotlin/androidx/build/metalava/MetalavaRunner.kt
index 89d64da..b7bbe88 100644
--- a/buildSrc/src/main/kotlin/androidx/build/metalava/MetalavaRunner.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/metalava/MetalavaRunner.kt
@@ -77,7 +77,6 @@
         "TopLevelBuilder",
         "MissingBuild",
         "BuilderSetStyle",
-        "SetterReturnsThis",
         "PackageLayering",
         "OverlappingConstants",
         "IllegalStateException",
@@ -98,7 +97,10 @@
         "MissingJvmStatic"
     ).joinToString(),
     "--error",
-    "MinMaxConstant"
+    listOf(
+        "MinMaxConstant",
+        "SetterReturnsThis"
+    ).joinToString()
 )
 
 sealed class GenerateApiMode {
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/ImageAnalysisTest.java b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/ImageAnalysisTest.java
index 387a960..3602ea9 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/ImageAnalysisTest.java
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/ImageAnalysisTest.java
@@ -43,6 +43,7 @@
 import androidx.camera.core.ImageAnalysisConfig;
 import androidx.camera.core.ImageProxy;
 import androidx.camera.core.UseCase.StateChangeListener;
+import androidx.camera.core.impl.utils.executor.CameraXExecutors;
 import androidx.camera.testing.CameraUtil;
 import androidx.test.annotation.UiThreadTest;
 import androidx.test.core.app.ApplicationProvider;
@@ -145,7 +146,7 @@
 
         Analyzer initialAnalyzer = useCase.getAnalyzer();
 
-        useCase.setAnalyzer(mMockAnalyzer);
+        useCase.setAnalyzer(CameraXExecutors.newHandlerExecutor(mHandler), mMockAnalyzer);
 
         Analyzer retrievedAnalyzer = useCase.getAnalyzer();
 
@@ -163,7 +164,7 @@
         useCase.updateSuggestedResolution(suggestedResolutionMap);
         useCase.addStateChangeListener(mMockListener);
 
-        useCase.setAnalyzer(mMockAnalyzer);
+        useCase.setAnalyzer(CameraXExecutors.newHandlerExecutor(mHandler), mMockAnalyzer);
 
         verify(mMockListener, times(1)).onUseCaseActive(useCase);
     }
@@ -176,7 +177,7 @@
         suggestedResolutionMap.put(mCameraId, DEFAULT_RESOLUTION);
         useCase.updateSuggestedResolution(suggestedResolutionMap);
         useCase.addStateChangeListener(mMockListener);
-        useCase.setAnalyzer(mMockAnalyzer);
+        useCase.setAnalyzer(CameraXExecutors.newHandlerExecutor(mHandler), mMockAnalyzer);
         useCase.removeAnalyzer();
 
         verify(mMockListener, times(1)).onUseCaseInactive(useCase);
@@ -197,9 +198,8 @@
     private void analyzerAnalyzesImagesWithMode(ImageReaderMode imageReaderMode)
             throws InterruptedException, CameraInfoUnavailableException {
         final int imageFormat = ImageFormat.YUV_420_888;
-        ImageAnalysisConfig config =
-                new ImageAnalysisConfig.Builder().setImageReaderMode(
-                        imageReaderMode).setCallbackHandler(mHandler).build();
+        ImageAnalysisConfig config = new ImageAnalysisConfig.Builder().setImageReaderMode(
+                imageReaderMode).build();
         ImageAnalysis useCase = new ImageAnalysis(config);
         Map<String, Size> suggestedResolutionMap = new HashMap<>();
         suggestedResolutionMap.put(mCameraId, DEFAULT_RESOLUTION);
@@ -209,7 +209,7 @@
         mInstrumentation.runOnMainSync(new Runnable() {
             @Override
             public void run() {
-                useCase.setAnalyzer(mAnalyzer);
+                useCase.setAnalyzer(CameraXExecutors.newHandlerExecutor(mHandler), mAnalyzer);
             }
         });
 
@@ -229,13 +229,12 @@
     @Test
     @UiThreadTest
     public void analyzerDoesNotAnalyzeImages_whenCameraIsNotOpen() throws InterruptedException {
-        ImageAnalysisConfig config =
-                new ImageAnalysisConfig.Builder().setCallbackHandler(mHandler).build();
+        ImageAnalysisConfig config = new ImageAnalysisConfig.Builder().build();
         ImageAnalysis useCase = new ImageAnalysis(config);
         Map<String, Size> suggestedResolutionMap = new HashMap<>();
         suggestedResolutionMap.put(mCameraId, DEFAULT_RESOLUTION);
         useCase.updateSuggestedResolution(suggestedResolutionMap);
-        useCase.setAnalyzer(mAnalyzer);
+        useCase.setAnalyzer(CameraXExecutors.newHandlerExecutor(mHandler), mAnalyzer);
         // Keep the lifecycle in an inactive state.
         // Wait a little while for frames to be analyzed.
         mAnalysisResultsSemaphore.tryAcquire(5, TimeUnit.SECONDS);
@@ -254,10 +253,9 @@
         final int imageFormat = ImageFormat.YUV_420_888;
         final Size[] sizes = {SECONDARY_RESOLUTION, DEFAULT_RESOLUTION};
 
-        ImageAnalysisConfig config =
-                new ImageAnalysisConfig.Builder().setCallbackHandler(mHandler).build();
+        ImageAnalysisConfig config = new ImageAnalysisConfig.Builder().build();
         ImageAnalysis useCase = new ImageAnalysis(config);
-        useCase.setAnalyzer(mAnalyzer);
+        useCase.setAnalyzer(CameraXExecutors.newHandlerExecutor(mHandler), mAnalyzer);
 
         for (Size size : sizes) {
             Map<String, Size> suggestedResolutionMap = new HashMap<>();
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/PreviewTest.java b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/PreviewTest.java
index 70ff115..cd28c8b 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/PreviewTest.java
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/PreviewTest.java
@@ -20,12 +20,15 @@
 import static com.google.common.truth.Truth.assertWithMessage;
 
 import static org.junit.Assume.assumeTrue;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.verify;
 
 import android.content.Context;
 import android.graphics.Rect;
 import android.graphics.SurfaceTexture;
+import android.os.AsyncTask;
 import android.os.Build;
 import android.util.Size;
 import android.view.Surface;
@@ -45,6 +48,7 @@
 import androidx.camera.core.SessionConfig;
 import androidx.camera.testing.CameraUtil;
 import androidx.camera.testing.fakes.FakeCameraControl;
+import androidx.camera.testing.fakes.FakeLifecycleOwner;
 import androidx.test.annotation.UiThreadTest;
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -62,11 +66,10 @@
 import java.util.List;
 import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
 import java.util.concurrent.FutureTask;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicReference;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -212,6 +215,7 @@
         final SurfaceTextureCallable surfaceTextureCallable0 = new SurfaceTextureCallable();
         final FutureTask<SurfaceTexture> future0 = new FutureTask<>(surfaceTextureCallable0);
         useCase.setOnPreviewOutputUpdateListener(
+                AsyncTask.SERIAL_EXECUTOR,
                 new OnPreviewOutputUpdateListener() {
                     @Override
                     public void onUpdated(@NonNull Preview.PreviewOutput previewOutput) {
@@ -228,6 +232,7 @@
         final SurfaceTextureCallable surfaceTextureCallable1 = new SurfaceTextureCallable();
         final FutureTask<SurfaceTexture> future1 = new FutureTask<>(surfaceTextureCallable1);
         useCase.setOnPreviewOutputUpdateListener(
+                AsyncTask.SERIAL_EXECUTOR,
                 new OnPreviewOutputUpdateListener() {
                     @Override
                     public void onUpdated(@NonNull Preview.PreviewOutput previewOutput) {
@@ -256,6 +261,7 @@
         final FutureTask<SurfaceTexture> future = new FutureTask<>(surfaceTextureCallable);
 
         useCase.setOnPreviewOutputUpdateListener(
+                AsyncTask.SERIAL_EXECUTOR,
                 new OnPreviewOutputUpdateListener() {
                     @Override
                     public void onUpdated(@NonNull Preview.PreviewOutput previewOutput) {
@@ -282,6 +288,7 @@
         final SurfaceTextureCallable surfaceTextureCallable0 = new SurfaceTextureCallable();
         final FutureTask<SurfaceTexture> future0 = new FutureTask<>(surfaceTextureCallable0);
         useCase.setOnPreviewOutputUpdateListener(
+                AsyncTask.SERIAL_EXECUTOR,
                 new OnPreviewOutputUpdateListener() {
                     @Override
                     public void onUpdated(@NonNull PreviewOutput previewOutput) {
@@ -297,6 +304,7 @@
         final SurfaceTextureCallable surfaceTextureCallable1 = new SurfaceTextureCallable();
         final FutureTask<SurfaceTexture> future1 = new FutureTask<>(surfaceTextureCallable1);
         useCase.setOnPreviewOutputUpdateListener(
+                AsyncTask.SERIAL_EXECUTOR,
                 new Preview.OnPreviewOutputUpdateListener() {
                     @Override
                     public void onUpdated(@NonNull Preview.PreviewOutput previewOutput) {
@@ -367,24 +375,30 @@
         Preview useCase = new Preview(mDefaultConfig);
         useCase.updateSuggestedResolution(Collections.singletonMap(mCameraId, DEFAULT_RESOLUTION));
 
-        final AtomicInteger calledCount = new AtomicInteger(0);
-        useCase.setOnPreviewOutputUpdateListener(
-                new Preview.OnPreviewOutputUpdateListener() {
-                    @Override
-                    public void onUpdated(@NonNull Preview.PreviewOutput previewOutput) {
-                        calledCount.incrementAndGet();
-                    }
-                });
-
-        int initialCount = calledCount.get();
+        useCase.setOnPreviewOutputUpdateListener(AsyncTask.SERIAL_EXECUTOR, mMockListener);
+        verify(mMockListener, timeout(3000)).onUpdated(any(PreviewOutput.class));
 
         useCase.updateSuggestedResolution(
                 Collections.singletonMap(mCameraId, SECONDARY_RESOLUTION));
 
-        int countAfterUpdate = calledCount.get();
+        verify(mMockListener, timeout(3000).times(2)).onUpdated(any(PreviewOutput.class));
+    }
 
-        assertThat(initialCount).isEqualTo(1);
-        assertThat(countAfterUpdate).isEqualTo(2);
+    @Test
+    @UiThreadTest
+    public void previewOutput_invokedByExecutor() {
+        Executor mockExecutor = mock(Executor.class);
+
+        Preview useCase = new Preview(mDefaultConfig);
+
+        FakeLifecycleOwner lifecycleOwner = new FakeLifecycleOwner();
+        lifecycleOwner.startAndResume();
+        CameraX.bindToLifecycle(lifecycleOwner, useCase);
+
+        useCase.setOnPreviewOutputUpdateListener(mockExecutor,
+                mock(OnPreviewOutputUpdateListener.class));
+
+        verify(mockExecutor, timeout(1000)).execute(any(Runnable.class));
     }
 
     @Test
@@ -394,24 +408,21 @@
         useCase.setTargetRotation(Surface.ROTATION_0);
         useCase.updateSuggestedResolution(Collections.singletonMap(mCameraId, DEFAULT_RESOLUTION));
 
-        final AtomicReference<PreviewOutput> latestPreviewOutput = new AtomicReference<>();
-        useCase.setOnPreviewOutputUpdateListener(
-                new OnPreviewOutputUpdateListener() {
-                    @Override
-                    public void onUpdated(@NonNull Preview.PreviewOutput previewOutput) {
-                        latestPreviewOutput.set(previewOutput);
-                    }
-                });
-
-        Preview.PreviewOutput initialOutput = latestPreviewOutput.get();
+        ArgumentCaptor<PreviewOutput> previewOutput = ArgumentCaptor.forClass(PreviewOutput.class);
+        useCase.setOnPreviewOutputUpdateListener(AsyncTask.SERIAL_EXECUTOR, mMockListener);
 
         useCase.setTargetRotation(Surface.ROTATION_90);
 
+        verify(mMockListener, timeout(3000).times(2)).onUpdated(previewOutput.capture());
+        assertThat(previewOutput.getAllValues()).hasSize(2);
+        Preview.PreviewOutput initialOutput = previewOutput.getAllValues().get(0);
+        Preview.PreviewOutput latestPreviewOutput = previewOutput.getAllValues().get(1);
+
         assertThat(initialOutput).isNotNull();
         assertThat(initialOutput.getSurfaceTexture())
-                .isEqualTo(latestPreviewOutput.get().getSurfaceTexture());
+                .isEqualTo(latestPreviewOutput.getSurfaceTexture());
         assertThat(initialOutput.getRotationDegrees())
-                .isNotEqualTo(latestPreviewOutput.get().getRotationDegrees());
+                .isNotEqualTo(latestPreviewOutput.getRotationDegrees());
     }
 
     @Test
@@ -423,6 +434,7 @@
         final SurfaceTextureCallable surfaceTextureCallable0 = new SurfaceTextureCallable();
         final FutureTask<SurfaceTexture> future0 = new FutureTask<>(surfaceTextureCallable0);
         useCase.setOnPreviewOutputUpdateListener(
+                AsyncTask.SERIAL_EXECUTOR,
                 new OnPreviewOutputUpdateListener() {
                     @Override
                     public void onUpdated(@NonNull Preview.PreviewOutput previewOutput) {
@@ -449,6 +461,7 @@
         useCase.updateSuggestedResolution(Collections.singletonMap(mCameraId, DEFAULT_RESOLUTION));
 
         useCase.setOnPreviewOutputUpdateListener(
+                AsyncTask.SERIAL_EXECUTOR,
                 new Preview.OnPreviewOutputUpdateListener() {
                     @Override
                     public void onUpdated(@NonNull PreviewOutput previewOutput) {
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/SensorOrientedMeteringPointFactoryTest.java b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/SensorOrientedMeteringPointFactoryTest.java
index 253cb85..e472f28 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/SensorOrientedMeteringPointFactoryTest.java
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/SensorOrientedMeteringPointFactoryTest.java
@@ -22,11 +22,10 @@
 
 import android.app.Instrumentation;
 import android.content.Context;
-import android.os.Handler;
-import android.os.Looper;
 import android.util.Rational;
 
 import androidx.camera.core.AppConfig;
+import androidx.camera.core.AspectRatio;
 import androidx.camera.core.CameraX;
 import androidx.camera.core.ImageAnalysis;
 import androidx.camera.core.ImageAnalysisConfig;
@@ -111,9 +110,8 @@
         ImageAnalysisConfig imageAnalysisConfig =
                 new ImageAnalysisConfig.Builder()
                         .setLensFacing(CameraX.LensFacing.BACK)
-                        .setTargetAspectRatio(new Rational(3, 4))
+                        .setTargetAspectRatio(AspectRatio.RATIO_4_3)
                         .setTargetName("ImageAnalysis")
-                        .setCallbackHandler(new Handler(Looper.getMainLooper()))
                         .build();
         ImageAnalysis imageAnalysis = new ImageAnalysis(imageAnalysisConfig);
         mInstrumentation.runOnMainSync(new Runnable() {
@@ -136,9 +134,8 @@
         ImageAnalysisConfig imageAnalysisConfig =
                 new ImageAnalysisConfig.Builder()
                         .setLensFacing(CameraX.LensFacing.BACK)
-                        .setTargetAspectRatio(new Rational(3, 4))
+                        .setTargetAspectRatio(AspectRatio.RATIO_4_3)
                         .setTargetName("ImageAnalysis")
-                        .setCallbackHandler(new Handler(Looper.getMainLooper()))
                         .build();
         ImageAnalysis imageAnalysis = new ImageAnalysis(imageAnalysisConfig);
 
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/UseCaseCombinationTest.java b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/UseCaseCombinationTest.java
index d1c2c3f..c27ca9d 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/UseCaseCombinationTest.java
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/UseCaseCombinationTest.java
@@ -23,9 +23,9 @@
 import android.Manifest;
 import android.app.Instrumentation;
 import android.content.Context;
-import android.os.Handler;
-import android.os.Looper;
+import android.graphics.SurfaceTexture;
 
+import androidx.annotation.NonNull;
 import androidx.camera.core.AppConfig;
 import androidx.camera.core.CameraX;
 import androidx.camera.core.CameraX.LensFacing;
@@ -36,7 +36,9 @@
 import androidx.camera.core.ImageProxy;
 import androidx.camera.core.Preview;
 import androidx.camera.core.PreviewConfig;
+import androidx.camera.core.impl.utils.executor.CameraXExecutors;
 import androidx.camera.testing.CameraUtil;
+import androidx.camera.testing.GLUtil;
 import androidx.camera.testing.fakes.FakeLifecycleOwner;
 import androidx.lifecycle.MutableLiveData;
 import androidx.lifecycle.Observer;
@@ -115,7 +117,7 @@
      * Test Combination: Preview + ImageCapture
      */
     @Test
-    public void previewCombinesImageCapture() {
+    public void previewCombinesImageCapture() throws InterruptedException {
         initPreview();
         initImageCapture();
 
@@ -123,10 +125,30 @@
             @Override
             public void run() {
                 CameraX.bindToLifecycle(mLifecycle, mPreview, mImageCapture);
+                mPreview.setOnPreviewOutputUpdateListener(
+                        new Preview.OnPreviewOutputUpdateListener() {
+                            @Override
+                            public void onUpdated(@NonNull Preview.PreviewOutput output) {
+                                output.getSurfaceTexture().attachToGLContext(
+                                        GLUtil.getTexIdFromGLContext());
+                                output.getSurfaceTexture().setOnFrameAvailableListener(
+                                        new SurfaceTexture.OnFrameAvailableListener() {
+                                            @Override
+                                            public void onFrameAvailable(
+                                                    SurfaceTexture surfaceTexture) {
+                                                surfaceTexture.updateTexImage();
+                                                mSemaphore.release();
+                                            }
+                                        });
+                            }
+                        });
                 mLifecycle.startAndResume();
             }
         });
 
+        // Wait for the frame available update.
+        mSemaphore.acquire(10);
+
         assertThat(mLifecycle.getObserverCount()).isEqualTo(2);
         assertThat(CameraX.isBound(mPreview)).isTrue();
         assertThat(CameraX.isBound(mImageCapture)).isTrue();
@@ -144,7 +166,7 @@
             @Override
             public void run() {
                 CameraX.bindToLifecycle(mLifecycle, mPreview, mImageAnalysis);
-                mImageAnalysis.setAnalyzer(mImageAnalyzer);
+                mImageAnalysis.setAnalyzer(CameraXExecutors.mainThreadExecutor(), mImageAnalyzer);
                 mAnalysisResult.observe(mLifecycle,
                         createCountIncrementingObserver());
                 mLifecycle.startAndResume();
@@ -171,7 +193,7 @@
             @Override
             public void run() {
                 CameraX.bindToLifecycle(mLifecycle, mPreview, mImageAnalysis, mImageCapture);
-                mImageAnalysis.setAnalyzer(mImageAnalyzer);
+                mImageAnalysis.setAnalyzer(CameraXExecutors.mainThreadExecutor(), mImageAnalyzer);
                 mAnalysisResult.observe(mLifecycle,
                         createCountIncrementingObserver());
                 mLifecycle.startAndResume();
@@ -192,7 +214,6 @@
                 new ImageAnalysisConfig.Builder()
                         .setLensFacing(DEFAULT_LENS_FACING)
                         .setTargetName("ImageAnalysis")
-                        .setCallbackHandler(new Handler(Looper.getMainLooper()))
                         .build();
         mImageAnalyzer =
                 new ImageAnalysis.Analyzer() {
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/impl/Camera2ImplCameraXTest.java b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/impl/Camera2ImplCameraXTest.java
index 0a7309f..a67fbe6 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/impl/Camera2ImplCameraXTest.java
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/impl/Camera2ImplCameraXTest.java
@@ -45,6 +45,7 @@
 import androidx.camera.core.ImageCapture;
 import androidx.camera.core.ImageCaptureConfig;
 import androidx.camera.core.ImageProxy;
+import androidx.camera.core.impl.utils.executor.CameraXExecutors;
 import androidx.camera.testing.CameraUtil;
 import androidx.camera.testing.fakes.FakeLifecycleOwner;
 import androidx.lifecycle.MutableLiveData;
@@ -143,7 +144,7 @@
                 ImageAnalysis useCase = new ImageAnalysis(builder.build());
 
                 CameraX.bindToLifecycle(mLifecycle, useCase);
-                useCase.setAnalyzer(mImageAnalyzer);
+                useCase.setAnalyzer(CameraXExecutors.mainThreadExecutor(), mImageAnalyzer);
                 mAnalysisResult.observe(mLifecycle, mockObserver);
 
                 mLifecycle.startAndResume();
@@ -184,8 +185,8 @@
 
                 CameraX.bindToLifecycle(mLifecycle, useCase, useCase2);
 
-                useCase.setAnalyzer(mImageAnalyzer);
-                useCase2.setAnalyzer(mImageAnalyzer2);
+                useCase.setAnalyzer(CameraXExecutors.mainThreadExecutor(), mImageAnalyzer);
+                useCase2.setAnalyzer(CameraXExecutors.mainThreadExecutor(), mImageAnalyzer2);
                 mAnalysisResult.observe(mLifecycle, mockObserver);
                 mAnalysisResult2.observe(mLifecycle, mockObserver2);
 
@@ -216,7 +217,7 @@
                         .setSessionCaptureCallback(sessionCaptureCallback);
                 ImageAnalysis useCase = new ImageAnalysis(configBuilder.build());
                 CameraX.bindToLifecycle(mLifecycle, useCase);
-                useCase.setAnalyzer(mImageAnalyzer);
+                useCase.setAnalyzer(CameraXExecutors.mainThreadExecutor(), mImageAnalyzer);
                 mAnalysisResult.observe(mLifecycle, createCountIncrementingObserver(observedCount));
 
                 mLifecycle.startAndResume();
@@ -259,7 +260,7 @@
             @Override
             public void run() {
                 CameraX.bindToLifecycle(mLifecycle, useCase);
-                useCase.setAnalyzer(mImageAnalyzer);
+                useCase.setAnalyzer(CameraXExecutors.mainThreadExecutor(), mImageAnalyzer);
                 mLifecycle.startAndResume();
             }
         });
@@ -362,7 +363,7 @@
                 @Override
                 public void run() {
                     CameraX.bindToLifecycle(mLifecycle, useCase);
-                    useCase.setAnalyzer(mImageAnalyzer);
+                    useCase.setAnalyzer(CameraXExecutors.mainThreadExecutor(), mImageAnalyzer);
                 }
             });
 
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/ImageAnalysisConfigProvider.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/ImageAnalysisConfigProvider.java
index 3719aab..2701a74 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/ImageAnalysisConfigProvider.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/ImageAnalysisConfigProvider.java
@@ -85,7 +85,7 @@
                     targetRotation);
             boolean isRotateNeeded = (rotationDegrees == 90 || rotationDegrees == 270);
             builder.setTargetRotation(targetRotation);
-            builder.setTargetAspectRatio(
+            builder.setTargetAspectRatioCustom(
                     isRotateNeeded ? DEFAULT_ASPECT_RATIO_3_4 : DEFAULT_ASPECT_RATIO_4_3);
         } catch (Exception e) {
             Log.w(TAG, "Unable to determine default lens facing for ImageAnalysis.", e);
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/ImageCaptureConfigProvider.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/ImageCaptureConfigProvider.java
index b8b2951..d6c8e574 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/ImageCaptureConfigProvider.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/ImageCaptureConfigProvider.java
@@ -85,7 +85,7 @@
                     targetRotation);
             boolean isRotateNeeded = (rotationDegrees == 90 || rotationDegrees == 270);
             builder.setTargetRotation(targetRotation);
-            builder.setTargetAspectRatio(
+            builder.setTargetAspectRatioCustom(
                     isRotateNeeded ? DEFAULT_ASPECT_RATIO_3_4 : DEFAULT_ASPECT_RATIO_4_3);
         } catch (Exception e) {
             Log.w(TAG, "Unable to determine default lens facing for ImageCapture.", e);
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/PreviewConfigProvider.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/PreviewConfigProvider.java
index e58ca95..3033be7 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/PreviewConfigProvider.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/PreviewConfigProvider.java
@@ -83,7 +83,7 @@
                     targetRotation);
             boolean isRotateNeeded = (rotationDegrees == 90 || rotationDegrees == 270);
             builder.setTargetRotation(targetRotation);
-            builder.setTargetAspectRatio(
+            builder.setTargetAspectRatioCustom(
                     isRotateNeeded ? DEFAULT_ASPECT_RATIO_3_4 : DEFAULT_ASPECT_RATIO_4_3);
         } catch (Exception e) {
             Log.w(TAG, "Unable to determine default lens facing for Preview.", e);
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/SupportedSurfaceCombination.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/SupportedSurfaceCombination.java
index a226491..bc8463d 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/SupportedSurfaceCombination.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/SupportedSurfaceCombination.java
@@ -26,11 +26,14 @@
 import android.hardware.camera2.params.StreamConfigurationMap;
 import android.media.CamcorderProfile;
 import android.os.Build;
+import android.util.Pair;
 import android.util.Rational;
 import android.util.Size;
 import android.view.Surface;
 import android.view.WindowManager;
 
+import androidx.annotation.Nullable;
+import androidx.camera.core.AspectRatio;
 import androidx.camera.core.CameraDeviceConfig;
 import androidx.camera.core.CameraInfoUnavailableException;
 import androidx.camera.core.CameraX;
@@ -71,6 +74,10 @@
     private static final Size QUALITY_720P_SIZE = new Size(1280, 720);
     private static final Size QUALITY_480P_SIZE = new Size(720, 480);
     private static final int ALIGN16 = 16;
+    private static final Rational ASPECT_RATIO_4_3 = new Rational(4, 3);
+    private static final Rational ASPECT_RATIO_3_4 = new Rational(3, 4);
+    private static final Rational ASPECT_RATIO_16_9 = new Rational(16, 9);
+    private static final Rational ASPECT_RATIO_9_16 = new Rational(9, 16);
     private final List<SurfaceCombination> mSurfaceCombinations = new ArrayList<>();
     private final Map<Integer, Size> mMaxSizeCache = new HashMap<>();
     private String mCameraId;
@@ -321,7 +328,7 @@
 
     private List<Size> getSupportedOutputSizes(UseCase useCase) {
         int imageFormat = useCase.getImageFormat();
-        Size[] outputSizes = getAllOutputSizesByFormat(imageFormat);
+        Size[] outputSizes = getAllOutputSizesByFormat(imageFormat, useCase);
         List<Size> outputSizeCandidates = new ArrayList<>();
         ImageOutputConfig config = (ImageOutputConfig) useCase.getUseCaseConfig();
         Size maxSize = config.getMaxResolution(getMaxOutputSizeByFormat(imageFormat));
@@ -330,10 +337,17 @@
         // result.
         Arrays.sort(outputSizes, new CompareSizesByArea(true));
 
-        // Filter out the ones that exceed the maximum size
+        // Get the minimum size according to min(DEFAULT_SIZE, TARGET_RESOLUTION).
+        Size targetSize = config.getTargetResolution(ZERO_SIZE);
+        Size minSize = DEFAULT_SIZE;
+        if (!targetSize.equals(ZERO_SIZE) && getArea(targetSize) < getArea(DEFAULT_SIZE)) {
+            minSize = targetSize;
+        }
+
+        // Filter out the ones that exceed the maximum size and the minimum size.
         for (Size outputSize : outputSizes) {
-            if (outputSize.getWidth() * outputSize.getHeight()
-                    <= maxSize.getWidth() * maxSize.getHeight()) {
+            if (getArea(outputSize) <= getArea(maxSize)
+                    && getArea(outputSize) >= getArea(minSize)) {
                 outputSizeCandidates.add(outputSize);
             }
         }
@@ -350,9 +364,28 @@
         List<Size> sizesMatchAspectRatio = new ArrayList<>();
         List<Size> sizesNotMatchAspectRatio = new ArrayList<>();
 
-        Rational aspectRatio = config.getTargetAspectRatio(null);
-        int targetRotation = config.getTargetRotation(Surface.ROTATION_0);
-        aspectRatio = rotateAspectRatioByRotation(aspectRatio, targetRotation);
+        Rational aspectRatio = null;
+        AspectRatio targetAspectRatio = config.getTargetAspectRatio(null);
+        if (targetAspectRatio != null) {
+            // Checks the sensor orientation.
+            boolean isSensorLandscapeOrientation = isRotationNeeded(Surface.ROTATION_0);
+            switch (targetAspectRatio) {
+                case RATIO_4_3:
+                    aspectRatio =
+                            isSensorLandscapeOrientation ? ASPECT_RATIO_4_3 : ASPECT_RATIO_3_4;
+                    break;
+                case RATIO_16_9:
+                    aspectRatio =
+                            isSensorLandscapeOrientation ? ASPECT_RATIO_16_9 : ASPECT_RATIO_9_16;
+                    break;
+                default:
+                    // Unhandled event.
+            }
+        } else {
+            aspectRatio = config.getTargetAspectRatioCustom(null);
+            int targetRotation = config.getTargetRotation(Surface.ROTATION_0);
+            aspectRatio = rotateAspectRatioByRotation(aspectRatio, targetRotation);
+        }
 
         for (Size outputSize : outputSizeCandidates) {
             // If target aspect ratio is set, moves the matched results to the front of the list.
@@ -372,9 +405,11 @@
         // Check whether the desired default resolution is included in the original supported list
         boolean isDefaultResolutionSupported = outputSizeCandidates.contains(DEFAULT_SIZE);
 
-        // If the target resolution is set, use it to find the minimum one from big enough items
-        Size targetSize = config.getTargetResolution(ZERO_SIZE);
+        // Check the default resolution if the target resolution is not set
+        targetSize = (targetSize.equals(ZERO_SIZE)) ? config.getDefaultResolution(ZERO_SIZE)
+                : targetSize;
 
+        // If the target resolution is set, use it to find the minimum one from big enough items
         if (!targetSize.equals(ZERO_SIZE)) {
             removeSupportedSizesByTargetSize(sizesMatchAspectRatio, targetSize);
             removeSupportedSizesByTargetSize(sizesNotMatchAspectRatio, targetSize);
@@ -405,6 +440,16 @@
     // Use target rotation to calibrate the aspect ratio.
     private Rational rotateAspectRatioByRotation(Rational aspectRatio, int targetRotation) {
         Rational outputRatio = aspectRatio;
+        // Calibrates the aspect ratio with the display and sensor rotation degrees values.
+        // Otherwise, retrieves default aspect ratio for the target use case.
+        if (aspectRatio != null && isRotationNeeded(targetRotation)) {
+            outputRatio = new Rational(aspectRatio.getDenominator(),
+                    aspectRatio.getNumerator());
+        }
+        return outputRatio;
+    }
+
+    private boolean isRotationNeeded(int targetRotation) {
         int sensorRotationDegrees;
         try {
             sensorRotationDegrees = CameraX.getCameraInfo(mCameraId).getSensorRotationDegrees(
@@ -412,13 +457,7 @@
         } catch (CameraInfoUnavailableException e) {
             throw new IllegalArgumentException("Unable to retrieve camera sensor orientation.", e);
         }
-        // Calibrates the aspect ratio with the display and sensor rotation degrees values.
-        // Otherwise, retrieves default aspect ratio for the target use case.
-        if (aspectRatio != null && (sensorRotationDegrees == 90 || sensorRotationDegrees == 270)) {
-            outputRatio = new Rational(aspectRatio.getDenominator(),
-                    aspectRatio.getNumerator());
-        }
-        return outputRatio;
+        return sensorRotationDegrees == 90 || sensorRotationDegrees == 270;
     }
 
     private boolean hasMatchingAspectRatio(Size resolution, Rational aspectRatio) {
@@ -451,6 +490,10 @@
         return false;
     }
 
+    private int getArea(Size size) {
+        return size.getWidth() * size.getHeight();
+    }
+
     private boolean ratioIntersectsMod16Segment(int height, int mod16Width, Rational aspectRatio) {
         Preconditions.checkArgument(mod16Width % 16 == 0);
         double aspectRatioWidth =
@@ -466,8 +509,7 @@
             // Get the index of the item that is big enough for the view size in matched list
             for (int i = 0; i < supportedSizesList.size(); i++) {
                 Size outputSize = supportedSizesList.get(i);
-                if (outputSize.getWidth() * outputSize.getHeight()
-                        >= targetSize.getWidth() * targetSize.getHeight()) {
+                if (getArea(outputSize) >= getArea(targetSize)) {
                     indexBigEnough = i;
                 } else {
                     break;
@@ -477,8 +519,7 @@
             Size bigEnoughSize = supportedSizesList.get(indexBigEnough);
             List<Size> removeSizes = new ArrayList<>();
             for (Size size : supportedSizesList) {
-                if (size.getWidth() * size.getHeight()
-                        > bigEnoughSize.getWidth() * bigEnoughSize.getHeight()) {
+                if (getArea(size) > getArea(bigEnoughSize)) {
                     removeSizes.add(size);
                 }
             }
@@ -541,32 +582,58 @@
         return allPossibleSizeArrangements;
     }
 
+    @Nullable
     private Size[] getAllOutputSizesByFormat(int imageFormat) {
-        if (mCharacteristics == null) {
-            throw new IllegalStateException("CameraCharacteristics is null.");
+        return getAllOutputSizesByFormat(imageFormat, null);
+    }
+
+    @Nullable
+    private Size[] getAllOutputSizesByFormat(int imageFormat, @Nullable UseCase useCase) {
+        Size[] outputSizes = null;
+
+        // Try to retrieve customized supported resolutions from config.
+        List<Pair<Integer, Size[]>> formatResolutionsPairList = null;
+
+        if (useCase != null) {
+            ImageOutputConfig imageOutputConfig = (ImageOutputConfig) useCase.getUseCaseConfig();
+
+            formatResolutionsPairList = imageOutputConfig.getSupportedResolutions(null);
         }
 
-        StreamConfigurationMap map =
-                mCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
-
-        if (map == null) {
-            throw new IllegalArgumentException(
-                    "Can not get supported output size for the format: " + imageFormat);
+        if (formatResolutionsPairList != null) {
+            for (Pair<Integer, Size[]> formatResolutionPair : formatResolutionsPairList) {
+                if (formatResolutionPair.first == imageFormat) {
+                    outputSizes = formatResolutionPair.second;
+                    break;
+                }
+            }
         }
 
-        Size[] outputSizes;
-        if (Build.VERSION.SDK_INT < 23
-                && imageFormat == ImageFormatConstants.INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE) {
-            // This is a little tricky that 0x22 that is internal defined in
-            // StreamConfigurationMap.java
-            // to be equal to ImageFormat.PRIVATE that is public after Android level 23 but not
-            // public in
-            // Android L. Use {@link SurfaceTexture} or {@link MediaCodec} will finally mapped to
-            // 0x22 in
-            // StreamConfigurationMap to retrieve the output sizes information.
-            outputSizes = map.getOutputSizes(SurfaceTexture.class);
-        } else {
-            outputSizes = map.getOutputSizes(imageFormat);
+        // Try to retrieve supported resolutions if there is no customization.
+        if (outputSizes == null) {
+            if (mCharacteristics == null) {
+                throw new IllegalStateException("CameraCharacteristics is null.");
+            }
+
+            StreamConfigurationMap map =
+                    mCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+
+            if (map == null) {
+                throw new IllegalArgumentException(
+                        "Can not get supported output size for the format: " + imageFormat);
+            }
+
+            if (Build.VERSION.SDK_INT < 23
+                    && imageFormat == ImageFormatConstants.INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE) {
+                // This is a little tricky that 0x22 that is internal defined in
+                // StreamConfigurationMap.java to be equal to ImageFormat.PRIVATE that is public
+                // after Android level 23 but not public in Android L. Use {@link SurfaceTexture}
+                // or {@link MediaCodec} will finally mapped to 0x22 in StreamConfigurationMap to
+                // retrieve the output sizes information.
+                outputSizes = map.getOutputSizes(SurfaceTexture.class);
+            } else {
+                outputSizes = map.getOutputSizes(imageFormat);
+            }
         }
 
         if (outputSizes == null) {
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/VideoCaptureConfigProvider.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/VideoCaptureConfigProvider.java
index 807ef4a..2a574db 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/VideoCaptureConfigProvider.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/VideoCaptureConfigProvider.java
@@ -85,7 +85,7 @@
                     targetRotation);
             boolean isRotateNeeded = (rotationDegrees == 90 || rotationDegrees == 270);
             builder.setTargetRotation(targetRotation);
-            builder.setTargetAspectRatio(
+            builder.setTargetAspectRatioCustom(
                     isRotateNeeded ? DEFAULT_ASPECT_RATIO_9_16 : DEFAULT_ASPECT_RATIO_16_9);
         } catch (Exception e) {
             Log.w(TAG, "Unable to determine default lens facing for VideoCapture.", e);
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/impl/Camera2DeviceSurfaceManagerTest.java b/camera/camera-camera2/src/test/java/androidx/camera/camera2/impl/Camera2DeviceSurfaceManagerTest.java
index 0422452..af1dd0c 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/impl/Camera2DeviceSurfaceManagerTest.java
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/impl/Camera2DeviceSurfaceManagerTest.java
@@ -35,6 +35,7 @@
 import android.view.WindowManager;
 
 import androidx.camera.core.AppConfig;
+import androidx.camera.core.AspectRatio;
 import androidx.camera.core.CameraDeviceSurfaceManager;
 import androidx.camera.core.CameraX;
 import androidx.camera.core.CameraX.LensFacing;
@@ -315,14 +316,13 @@
 
     @Test
     public void suggestedResolutionsForMixedUseCaseNotSupportedInLegacyDevice() {
-        Rational aspectRatio = new Rational(16, 9);
         PreviewConfig.Builder previewConfigBuilder = new PreviewConfig.Builder();
         VideoCaptureConfig.Builder videoCaptureConfigBuilder = new VideoCaptureConfig.Builder();
         ImageCaptureConfig.Builder imageCaptureConfigBuilder = new ImageCaptureConfig.Builder();
 
-        previewConfigBuilder.setTargetAspectRatio(aspectRatio);
-        videoCaptureConfigBuilder.setTargetAspectRatio(aspectRatio);
-        imageCaptureConfigBuilder.setTargetAspectRatio(aspectRatio);
+        previewConfigBuilder.setTargetAspectRatio(AspectRatio.RATIO_16_9);
+        videoCaptureConfigBuilder.setTargetAspectRatio(AspectRatio.RATIO_16_9);
+        imageCaptureConfigBuilder.setTargetAspectRatio(AspectRatio.RATIO_16_9);
 
         imageCaptureConfigBuilder.setLensFacing(LensFacing.FRONT);
         ImageCapture imageCapture = new ImageCapture(imageCaptureConfigBuilder.build());
@@ -350,14 +350,13 @@
 
     @Test
     public void getSuggestedResolutionsForMixedUseCaseInLimitedDevice() {
-        Rational aspectRatio = new Rational(9, 16);
         PreviewConfig.Builder previewConfigBuilder = new PreviewConfig.Builder();
         VideoCaptureConfig.Builder videoCaptureConfigBuilder = new VideoCaptureConfig.Builder();
         ImageCaptureConfig.Builder imageCaptureConfigBuilder = new ImageCaptureConfig.Builder();
 
-        previewConfigBuilder.setTargetAspectRatio(aspectRatio);
-        videoCaptureConfigBuilder.setTargetAspectRatio(aspectRatio);
-        imageCaptureConfigBuilder.setTargetAspectRatio(aspectRatio);
+        previewConfigBuilder.setTargetAspectRatio(AspectRatio.RATIO_16_9);
+        videoCaptureConfigBuilder.setTargetAspectRatio(AspectRatio.RATIO_16_9);
+        imageCaptureConfigBuilder.setTargetAspectRatio(AspectRatio.RATIO_16_9);
 
         imageCaptureConfigBuilder.setLensFacing(LensFacing.BACK);
         ImageCapture imageCapture = new ImageCapture(imageCaptureConfigBuilder.build());
@@ -496,7 +495,7 @@
         Rational targetAspectRatio = new Rational(9, 16);
         PreviewConfig.Builder previewConfigBuilder = new PreviewConfig.Builder();
 
-        previewConfigBuilder.setTargetAspectRatio(targetAspectRatio);
+        previewConfigBuilder.setTargetAspectRatio(AspectRatio.RATIO_16_9);
         previewConfigBuilder.setLensFacing(LensFacing.FRONT);
         PreviewConfig previewConfig = previewConfigBuilder.build();
 
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/impl/SupportedSurfaceCombinationTest.java b/camera/camera-camera2/src/test/java/androidx/camera/camera2/impl/SupportedSurfaceCombinationTest.java
index 9861dc2..fa669bd 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/impl/SupportedSurfaceCombinationTest.java
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/impl/SupportedSurfaceCombinationTest.java
@@ -31,12 +31,14 @@
 import android.hardware.camera2.CameraManager;
 import android.hardware.camera2.CameraMetadata;
 import android.os.Build;
+import android.util.Pair;
 import android.util.Rational;
 import android.util.Size;
 import android.view.WindowManager;
 
 import androidx.camera.camera2.Camera2AppConfig;
 import androidx.camera.core.AppConfig;
+import androidx.camera.core.AspectRatio;
 import androidx.camera.core.CameraX;
 import androidx.camera.core.CameraX.LensFacing;
 import androidx.camera.core.ImageAnalysis;
@@ -75,6 +77,7 @@
 import org.robolectric.shadows.ShadowCameraManager;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ExecutionException;
@@ -92,9 +95,10 @@
     private static final String LEVEL3_CAMERA_ID = "4";
     private static final int DEFAULT_SENSOR_ORIENTATION = 90;
     private static final Rational ASPECT_RATIO_4_3 = new Rational(4, 3);
-    private final Size mDisplaySize = new Size(1280, 720);
+    private static final Rational ASPECT_RATIO_16_9 = new Rational(16, 9);
+    private final Size mDisplaySize = new Size(720, 1280);
     private final Size mAnalysisSize = new Size(640, 480);
-    private final Size mPreviewSize = mDisplaySize;
+    private final Size mPreviewSize = new Size(1280, 720);
     private final Size mRecordSize = new Size(3840, 2160);
     private final Size mMaximumSize = new Size(4032, 3024);
     private final Size mMaximumVideoSize = new Size(1920, 1080);
@@ -132,9 +136,11 @@
                     new Size(1280, 720),
                     new Size(1280, 720), // duplicate the size since Nexus 5X emulator has the case.
                     new Size(960, 544), // a mod16 version of resolution with 16:9 aspect ratio.
+                    new Size(800, 450),
                     new Size(640, 480),
                     new Size(320, 240),
-                    new Size(320, 180)
+                    new Size(320, 180),
+                    new Size(256, 144) // For checkSmallSizesAreFilteredOut test.
             };
 
     private final Context mContext = RuntimeEnvironment.application.getApplicationContext();
@@ -463,33 +469,41 @@
         Rational targetAspectRatio = new Rational(9, 16);
         PreviewConfig.Builder previewConfigBuilder = new PreviewConfig.Builder();
 
-        previewConfigBuilder.setTargetAspectRatio(targetAspectRatio);
+        previewConfigBuilder.setTargetAspectRatio(AspectRatio.RATIO_16_9);
         previewConfigBuilder.setLensFacing(LensFacing.FRONT);
         Preview preview = new Preview(previewConfigBuilder.build());
 
         PreviewConfig config = (PreviewConfig) preview.getUseCaseConfig();
-        Rational previewAspectRatio = config.getTargetAspectRatio();
+        Rational previewAspectRatio = config.getTargetAspectRatioCustom();
 
-        Rational resultAspectRatio = supportedSurfaceCombination.getCorrectedAspectRatio(config);
+        Rational correctedAspectRatio = supportedSurfaceCombination.getCorrectedAspectRatio(config);
 
         Size maxJpegSize = supportedSurfaceCombination.getMaxOutputSizeByFormat(ImageFormat.JPEG);
         Rational maxJpegAspectRatio = new Rational(maxJpegSize.getHeight(), maxJpegSize.getWidth());
 
+        List<UseCase> useCases = new ArrayList<>();
+        useCases.add(preview);
+        Map<UseCase, Size> suggestedResolutionMap =
+                supportedSurfaceCombination.getSuggestedResolutions(null, useCases);
+        Size previewSize = suggestedResolutionMap.get(preview);
+        Rational resultAspectRatio = new Rational(previewSize.getHeight(), previewSize.getWidth());
+
         if (Build.VERSION.SDK_INT == 21) {
             // Checks targetAspectRatio and maxJpegAspectRatio, which is the ratio of maximum size
             // in the mSupportedSizes, are not equal to make sure this test case is valid.
             assertFalse(targetAspectRatio.equals(maxJpegAspectRatio));
             assertTrue(previewAspectRatio.equals(maxJpegAspectRatio));
+            assertTrue(correctedAspectRatio.equals(maxJpegAspectRatio));
             assertTrue(resultAspectRatio.equals(maxJpegAspectRatio));
         } else {
             // Checks no correction is needed.
-            assertThat(resultAspectRatio).isNull();
-            assertTrue(previewAspectRatio.equals(targetAspectRatio));
+            assertThat(correctedAspectRatio).isNull();
+            assertTrue(resultAspectRatio.equals(targetAspectRatio));
         }
     }
 
     @Test
-    public void checkDefaultAspectRatioForMixedUseCase() {
+    public void checkDefaultAspectRatioAndResolutionForMixedUseCase() {
         setupCamera(/* supportsRaw= */ false);
         SupportedSurfaceCombination supportedSurfaceCombination =
                 new SupportedSurfaceCombination(
@@ -524,9 +538,13 @@
         Rational imageAnalysisAspectRatio = new Rational(imageAnalysisSize.getWidth(),
                 imageAnalysisSize.getHeight());
 
+        // Checks the default aspect ratio.
         assertTrue(previewAspectRatio.equals(ASPECT_RATIO_4_3));
         assertTrue(imageCaptureAspectRatio.equals(ASPECT_RATIO_4_3));
         assertTrue(imageAnalysisAspectRatio.equals(ASPECT_RATIO_4_3));
+
+        // Checks the default resolution.
+        assertTrue(imageAnalysisSize.equals(mAnalysisSize));
     }
 
     @Test
@@ -558,20 +576,84 @@
     }
 
     @Test
+    public void checkSmallSizesAreFilteredOutByDefaultSize480p() {
+        setupCamera(/* supportsRaw= */ false);
+        SupportedSurfaceCombination supportedSurfaceCombination =
+                new SupportedSurfaceCombination(
+                        mContext, LIMITED_CAMERA_ID, mMockCamcorderProfileHelper);
+        /* This test case is for b/139018208 that get small resolution 144x256 with below
+        conditions:
+        1. The target aspect ratio is set to the screen size 1080 x 2220 (9:18.5).
+        2. The camera doesn't provide any 9:18.5 resolution and the size 144x256(9:16)
+         is considered the 9:18.5 mod16 version.
+        3. There is no other bigger resolution matched the target aspect ratio.
+        */
+        final int displayWidth = 1080;
+        final int displayHeight = 2220;
+        PreviewConfig.Builder previewConfigBuilder = new PreviewConfig.Builder();
+        previewConfigBuilder.setTargetResolution(new Size(displayHeight, displayWidth));
+        Preview preview = new Preview(previewConfigBuilder.build());
+
+        List<UseCase> useCases = new ArrayList<>();
+        useCases.add(preview);
+        Map<UseCase, Size> suggestedResolutionMap =
+                supportedSurfaceCombination.getSuggestedResolutions(null, useCases);
+
+        // Checks the preconditions.
+        final Size preconditionSize = new Size(256, 144);
+        final Rational targetRatio = new Rational(displayHeight, displayWidth);
+        ArrayList<Size> sizeList = new ArrayList<>(Arrays.asList(mSupportedSizes));
+        assertTrue(sizeList.contains(preconditionSize));
+        for (Size s : mSupportedSizes) {
+            Rational supportedRational = new Rational(s.getWidth(), s.getHeight());
+            assertFalse(supportedRational.equals(targetRatio));
+        }
+
+        // Checks the mechanism has filtered out the sizes which are smaller than default size 480p.
+        Size previewSize = suggestedResolutionMap.get(preview);
+        assertTrue(!previewSize.equals(preconditionSize));
+    }
+
+    @Test
+    public void checkSmallTargetResolutionIsNotFilteredOutBy480p() {
+        setupCamera(/* supportsRaw= */ false);
+        SupportedSurfaceCombination supportedSurfaceCombination =
+                new SupportedSurfaceCombination(
+                        mContext, LIMITED_CAMERA_ID, mMockCamcorderProfileHelper);
+
+        // The resolution selection will filter out the sizes which are smaller than min(640x480,
+        // TARGET_RESOLUTION)
+        final Size targetResolution = new Size(240, 320);
+        PreviewConfig.Builder previewConfigBuilder = new PreviewConfig.Builder();
+        previewConfigBuilder.setTargetResolution(targetResolution);
+        Preview preview = new Preview(previewConfigBuilder.build());
+
+        List<UseCase> useCases = new ArrayList<>();
+        useCases.add(preview);
+        Map<UseCase, Size> suggestedResolutionMap =
+                supportedSurfaceCombination.getSuggestedResolutions(null, useCases);
+
+        // Checks the returned size is 320x240, if we set the target resolution which is smaller
+        // than 480p.
+        final Size resultSize = new Size(320, 240);
+        Size previewSize = suggestedResolutionMap.get(preview);
+        assertTrue(previewSize.equals(resultSize));
+    }
+
+    @Test
     public void suggestedResolutionsForMixedUseCaseNotSupportedInLegacyDevice() {
         setupCamera(/* supportsRaw= */ false);
         SupportedSurfaceCombination supportedSurfaceCombination =
                 new SupportedSurfaceCombination(
                         mContext, LEGACY_CAMERA_ID, mMockCamcorderProfileHelper);
 
-        Rational aspectRatio = new Rational(16, 9);
         PreviewConfig.Builder previewConfigBuilder = new PreviewConfig.Builder();
         VideoCaptureConfig.Builder videoCaptureConfigBuilder = new VideoCaptureConfig.Builder();
         ImageCaptureConfig.Builder imageCaptureConfigBuilder = new ImageCaptureConfig.Builder();
 
-        previewConfigBuilder.setTargetAspectRatio(aspectRatio);
-        videoCaptureConfigBuilder.setTargetAspectRatio(aspectRatio);
-        imageCaptureConfigBuilder.setTargetAspectRatio(aspectRatio);
+        previewConfigBuilder.setTargetAspectRatio(AspectRatio.RATIO_16_9);
+        videoCaptureConfigBuilder.setTargetAspectRatio(AspectRatio.RATIO_16_9);
+        imageCaptureConfigBuilder.setTargetAspectRatio(AspectRatio.RATIO_16_9);
 
         imageCaptureConfigBuilder.setLensFacing(LensFacing.FRONT);
         ImageCapture imageCapture = new ImageCapture(imageCaptureConfigBuilder.build());
@@ -597,14 +679,13 @@
                 new SupportedSurfaceCombination(
                         mContext, LIMITED_CAMERA_ID, mMockCamcorderProfileHelper);
 
-        Rational aspectRatio = new Rational(9, 16);
         PreviewConfig.Builder previewConfigBuilder = new PreviewConfig.Builder();
         VideoCaptureConfig.Builder videoCaptureConfigBuilder = new VideoCaptureConfig.Builder();
         ImageCaptureConfig.Builder imageCaptureConfigBuilder = new ImageCaptureConfig.Builder();
 
-        previewConfigBuilder.setTargetAspectRatio(aspectRatio);
-        videoCaptureConfigBuilder.setTargetAspectRatio(aspectRatio);
-        imageCaptureConfigBuilder.setTargetAspectRatio(aspectRatio);
+        previewConfigBuilder.setTargetAspectRatio(AspectRatio.RATIO_16_9);
+        videoCaptureConfigBuilder.setTargetAspectRatio(AspectRatio.RATIO_16_9);
+        imageCaptureConfigBuilder.setTargetAspectRatio(AspectRatio.RATIO_16_9);
 
         imageCaptureConfigBuilder.setLensFacing(LensFacing.BACK);
         ImageCapture imageCapture = new ImageCapture(imageCaptureConfigBuilder.build());
@@ -638,7 +719,6 @@
         1. There are duplicated two 1280x720 supported sizes for ImageCapture and Preview.
         2. supportedOutputSizes for ImageCapture and Preview in
         SupportedSurfaceCombination#getAllPossibleSizeArrangements are the same.
-        3. Target aspect ratio is 3:4.
         */
         ImageCaptureConfig.Builder imageCaptureConfigBuilder = new ImageCaptureConfig.Builder();
         PreviewConfig.Builder previewConfigBuilder = new PreviewConfig.Builder();
@@ -646,9 +726,9 @@
 
         imageCaptureConfigBuilder.setTargetResolution(mDisplaySize);
         ImageCapture imageCapture = new ImageCapture(imageCaptureConfigBuilder.build());
-        previewConfigBuilder.setTargetAspectRatio(new Rational(3, 4)).setTargetResolution(
-                mDisplaySize).setLensFacing(LensFacing.BACK);
+        previewConfigBuilder.setTargetResolution(mDisplaySize).setLensFacing(LensFacing.BACK);
         Preview preview = new Preview(previewConfigBuilder.build());
+        imageAnalysisConfigBuilder.setTargetResolution(mDisplaySize).setLensFacing(LensFacing.BACK);
         ImageAnalysis imageAnalysis = new ImageAnalysis(imageAnalysisConfigBuilder.build());
 
         List<UseCase> useCases = new ArrayList<>();
@@ -658,9 +738,133 @@
         Map<UseCase, Size> suggestedResolutionMap =
                 supportedSurfaceCombination.getSuggestedResolutions(null, useCases);
 
-        assertThat(suggestedResolutionMap).containsEntry(imageCapture, mMaximumSize);
+        assertThat(suggestedResolutionMap).containsEntry(imageCapture, mPreviewSize);
+        assertThat(suggestedResolutionMap).containsEntry(preview, mPreviewSize);
+        assertThat(suggestedResolutionMap).containsEntry(imageAnalysis, mPreviewSize);
+    }
+
+    @Test
+    public void setTargetAspectRatioForMixedUseCases() {
+        setupCamera(/* supportsRaw= */ false);
+        SupportedSurfaceCombination supportedSurfaceCombination =
+                new SupportedSurfaceCombination(
+                        mContext, FULL_CAMERA_ID, mMockCamcorderProfileHelper);
+
+        PreviewConfig.Builder previewConfigBuilder = new PreviewConfig.Builder();
+        ImageCaptureConfig.Builder imageCaptureConfigBuilder = new ImageCaptureConfig.Builder();
+        ImageAnalysisConfig.Builder imageAnalysisConfigBuilder = new ImageAnalysisConfig.Builder();
+
+        previewConfigBuilder.setTargetAspectRatio(AspectRatio.RATIO_16_9);
+        imageCaptureConfigBuilder.setTargetAspectRatio(AspectRatio.RATIO_16_9);
+        imageAnalysisConfigBuilder.setTargetAspectRatio(AspectRatio.RATIO_16_9);
+
+        Preview preview = new Preview(previewConfigBuilder.build());
+        ImageCapture imageCapture = new ImageCapture(imageCaptureConfigBuilder.build());
+        ImageAnalysis imageAnalysis = new ImageAnalysis(imageAnalysisConfigBuilder.build());
+
+        List<UseCase> useCases = new ArrayList<>();
+        useCases.add(imageCapture);
+        useCases.add(preview);
+        useCases.add(imageAnalysis);
+        Map<UseCase, Size> suggestedResolutionMap =
+                supportedSurfaceCombination.getSuggestedResolutions(null, useCases);
+
+        Size previewSize = suggestedResolutionMap.get(preview);
+        Size imageCaptureSize = suggestedResolutionMap.get(imageCapture);
+        Size imageAnalysisSize = suggestedResolutionMap.get(imageAnalysis);
+
+        Rational previewAspectRatio = new Rational(previewSize.getWidth(), previewSize.getHeight());
+        Rational imageCaptureAspectRatio = new Rational(imageCaptureSize.getWidth(),
+                imageCaptureSize.getHeight());
+        Rational imageAnalysisAspectRatio = new Rational(imageAnalysisSize.getWidth(),
+                imageAnalysisSize.getHeight());
+
+        assertTrue(previewAspectRatio.equals(ASPECT_RATIO_16_9));
+        assertTrue(imageCaptureAspectRatio.equals(ASPECT_RATIO_16_9));
+        assertTrue(imageAnalysisAspectRatio.equals(ASPECT_RATIO_16_9));
+    }
+
+    @Test
+    public void throwsWhenSetBothTargetResolutionAndAspectRatioForDifferentUseCases() {
+        setupCamera(/* supportsRaw= */ false);
+
+        boolean previewExceptionHappened = false;
+        PreviewConfig.Builder previewConfigBuilder = new PreviewConfig.Builder()
+                .setLensFacing(LensFacing.BACK)
+                .setTargetResolution(mDisplaySize)
+                .setTargetAspectRatio(AspectRatio.RATIO_16_9);
+        try {
+            previewConfigBuilder.build();
+        } catch (IllegalArgumentException e) {
+            previewExceptionHappened = true;
+        }
+        assertTrue(previewExceptionHappened);
+
+        boolean imageCaptureExceptionHappened = false;
+        ImageCaptureConfig.Builder imageCaptureConfigBuilder = new ImageCaptureConfig.Builder()
+                .setLensFacing(LensFacing.BACK)
+                .setTargetResolution(mDisplaySize)
+                .setTargetAspectRatio(AspectRatio.RATIO_16_9);
+        try {
+            imageCaptureConfigBuilder.build();
+        } catch (IllegalArgumentException e) {
+            imageCaptureExceptionHappened = true;
+        }
+        assertTrue(imageCaptureExceptionHappened);
+
+        boolean imageAnalysisExceptionHappened = false;
+        ImageAnalysisConfig.Builder imageAnalysisConfigBuilder = new ImageAnalysisConfig.Builder()
+                .setLensFacing(LensFacing.BACK)
+                .setTargetResolution(mDisplaySize)
+                .setTargetAspectRatio(AspectRatio.RATIO_16_9);
+        try {
+            imageAnalysisConfigBuilder.build();
+        } catch (IllegalArgumentException e) {
+            imageAnalysisExceptionHappened = true;
+        }
+        assertTrue(imageAnalysisExceptionHappened);
+    }
+
+    @Test
+    public void getSuggestedResolutionsForCustomizedSupportedResolutions() {
+        setupCamera(/* supportsRaw= */ false);
+        SupportedSurfaceCombination supportedSurfaceCombination =
+                new SupportedSurfaceCombination(
+                        mContext, LIMITED_CAMERA_ID, mMockCamcorderProfileHelper);
+
+        PreviewConfig.Builder previewConfigBuilder = new PreviewConfig.Builder();
+        VideoCaptureConfig.Builder videoCaptureConfigBuilder = new VideoCaptureConfig.Builder();
+        ImageCaptureConfig.Builder imageCaptureConfigBuilder = new ImageCaptureConfig.Builder();
+
+        List<Pair<Integer, Size[]>> formatResolutionsPairList = new ArrayList<>();
+        formatResolutionsPairList.add(Pair.create(ImageFormat.JPEG, new Size[]{mAnalysisSize}));
+        formatResolutionsPairList.add(
+                Pair.create(ImageFormat.YUV_420_888, new Size[]{mAnalysisSize}));
+        formatResolutionsPairList.add(Pair.create(ImageFormat.PRIVATE, new Size[]{mAnalysisSize}));
+
+        // Sets customized supported resolutions to 640x480 only.
+        imageCaptureConfigBuilder.setSupportedResolutions(formatResolutionsPairList);
+        videoCaptureConfigBuilder.setSupportedResolutions(formatResolutionsPairList);
+        previewConfigBuilder.setSupportedResolutions(formatResolutionsPairList);
+
+        imageCaptureConfigBuilder.setLensFacing(LensFacing.BACK);
+        ImageCapture imageCapture = new ImageCapture(imageCaptureConfigBuilder.build());
+        videoCaptureConfigBuilder.setLensFacing(LensFacing.BACK);
+        VideoCapture videoCapture = new VideoCapture(videoCaptureConfigBuilder.build());
+        previewConfigBuilder.setLensFacing(LensFacing.BACK);
+        Preview preview = new Preview(previewConfigBuilder.build());
+
+        List<UseCase> useCases = new ArrayList<>();
+        useCases.add(imageCapture);
+        useCases.add(videoCapture);
+        useCases.add(preview);
+        Map<UseCase, Size> suggestedResolutionMap =
+                supportedSurfaceCombination.getSuggestedResolutions(null, useCases);
+
+        // Checks all suggested resolutions will become 640x480.
+        assertThat(suggestedResolutionMap).containsEntry(imageCapture, mAnalysisSize);
+        assertThat(suggestedResolutionMap).containsEntry(videoCapture, mAnalysisSize);
         assertThat(suggestedResolutionMap).containsEntry(preview, mAnalysisSize);
-        assertThat(suggestedResolutionMap).containsEntry(imageAnalysis, mAnalysisSize);
     }
 
     @Test
@@ -826,18 +1030,17 @@
                 new SupportedSurfaceCombination(
                         mContext, LEGACY_CAMERA_ID, mMockCamcorderProfileHelper);
 
-        Rational aspectRatio = new Rational(9, 16);
         PreviewConfig.Builder previewConfigBuilder = new PreviewConfig.Builder();
         ImageCaptureConfig.Builder imageCaptureConfigBuilder = new ImageCaptureConfig.Builder();
 
         previewConfigBuilder.setLensFacing(LensFacing.BACK);
         imageCaptureConfigBuilder.setLensFacing(LensFacing.BACK);
 
-        previewConfigBuilder.setTargetAspectRatio(aspectRatio);
-        imageCaptureConfigBuilder.setTargetAspectRatio(aspectRatio);
+        previewConfigBuilder.setTargetAspectRatio(AspectRatio.RATIO_16_9);
+        imageCaptureConfigBuilder.setTargetAspectRatio(AspectRatio.RATIO_16_9);
 
-        previewConfigBuilder.setTargetResolution(mMod16Size);
-        imageCaptureConfigBuilder.setTargetResolution(mMod16Size);
+        previewConfigBuilder.setDefaultResolution(mMod16Size);
+        imageCaptureConfigBuilder.setDefaultResolution(mMod16Size);
 
         Preview preview = new Preview(previewConfigBuilder.build());
         ImageCapture imageCapture = new ImageCapture(imageCaptureConfigBuilder.build());
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/AspectRatio.java b/camera/camera-core/src/main/java/androidx/camera/core/AspectRatio.java
new file mode 100644
index 0000000..ac49a42
--- /dev/null
+++ b/camera/camera-core/src/main/java/androidx/camera/core/AspectRatio.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2019 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.camera.core;
+
+/** The aspect ratio of the use case. */
+public enum AspectRatio {
+    /** 4:3 standard aspect ratio. */
+    RATIO_4_3,
+    /** 16:9 standard aspect ratio. */
+    RATIO_16_9
+}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysis.java b/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysis.java
index 483e17c..06f9ed9 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysis.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysis.java
@@ -17,8 +17,6 @@
 package androidx.camera.core;
 
 import android.media.ImageReader;
-import android.os.Handler;
-import android.os.Looper;
 import android.util.Log;
 import android.util.Size;
 import android.view.Display;
@@ -62,8 +60,8 @@
     private static final int NON_BLOCKING_IMAGE_DEPTH = 4;
 
     final AtomicReference<Analyzer> mSubscribedAnalyzer;
+    final AtomicReference<Executor> mAnalyzerExecutor;
     final AtomicInteger mRelativeRotation = new AtomicInteger();
-    final Handler mHandler;
     private final ImageAnalysisConfig.Builder mUseCaseConfigBuilder;
     @SuppressWarnings("WeakerAccess") /* synthetic access */
     final ImageAnalysisBlockingAnalyzer mImageAnalysisBlockingAnalyzer;
@@ -86,18 +84,15 @@
         // Get the combined configuration with defaults
         ImageAnalysisConfig combinedConfig = (ImageAnalysisConfig) getUseCaseConfig();
         mSubscribedAnalyzer = new AtomicReference<>();
-        mHandler = combinedConfig.getCallbackHandler(null);
-        if (mHandler == null) {
-            throw new IllegalStateException("No default mHandler specified.");
-        }
+        mAnalyzerExecutor = new AtomicReference<>();
         setImageFormat(ImageReaderFormatRecommender.chooseCombo().imageAnalysisFormat());
         // Init both instead of lazy loading to void synchronization.
         mImageAnalysisBlockingAnalyzer = new ImageAnalysisBlockingAnalyzer(mSubscribedAnalyzer,
                 mRelativeRotation,
-                mHandler);
+                mAnalyzerExecutor);
         mImageAnalysisNonBlockingAnalyzer = new ImageAnalysisNonBlockingAnalyzer(
                 mSubscribedAnalyzer, mRelativeRotation,
-                mHandler, config.getBackgroundExecutor(
+                mAnalyzerExecutor, config.getBackgroundExecutor(
                 CameraXExecutors.highPriorityExecutor()));
     }
 
@@ -184,15 +179,17 @@
     /**
      * Removes a previously set analyzer.
      *
-     * <p>This is equivalent to calling {@code setAnalyzer(null)}.  This will stop data from
-     * streaming to the {@link ImageAnalysis}.
+     * <p>This will stop data from streaming to the {@link ImageAnalysis}.
      *
      * @throws IllegalStateException If not called on main thread.
      */
     @UiThread
     public void removeAnalyzer() {
         Threads.checkMainThread();
-        setAnalyzer(null);
+        mAnalyzerExecutor.set(null);
+        if (mSubscribedAnalyzer.getAndSet(null) != null) {
+            notifyInactive();
+        }
     }
 
     /**
@@ -266,8 +263,7 @@
      * Sets an analyzer to receive and analyze images.
      *
      * <p>Setting an analyzer will signal to the camera that it should begin sending data. The
-     * stream of data can be stopped by setting the analyzer to {@code null} or by calling {@link
-     * #removeAnalyzer()}.
+     * stream of data can be stopped by calling {@link #removeAnalyzer()}.
      *
      * <p>Applications can process or copy the image by implementing the {@link Analyzer}.  If
      * frames should be skipped (no analysis), the analyzer function should return, instead of
@@ -276,18 +272,18 @@
      * <p>Setting an analyzer function replaces any previous analyzer.  Only one analyzer can be
      * set at any time.
      *
-     * @param analyzer of the images or {@code null} to stop data streaming to
-     *                 {@link ImageAnalysis}.
+     * @param executor The executor in which the
+     * {@link ImageAnalysis.Analyzer#analyze(ImageProxy, int)} will be run.
+     * @param analyzer of the images.
      * @throws IllegalStateException If not called on main thread.
      */
     @UiThread
-    public void setAnalyzer(@Nullable Analyzer analyzer) {
+    public void setAnalyzer(@NonNull Executor executor, @NonNull Analyzer analyzer) {
         Threads.checkMainThread();
+        mAnalyzerExecutor.set(executor);
         Analyzer previousAnalyzer = mSubscribedAnalyzer.getAndSet(analyzer);
         if (previousAnalyzer == null && analyzer != null) {
             notifyActive();
-        } else if (previousAnalyzer != null && analyzer == null) {
-            notifyInactive();
         }
     }
 
@@ -385,8 +381,8 @@
     /**
      * Interface for analyzing images.
      *
-     * <p>Implement Analyzer and pass it to {@link ImageAnalysis#setAnalyzer(Analyzer)} to receive
-     * images and perform custom processing by implementing the
+     * <p>Implement Analyzer and pass it to {@link ImageAnalysis#setAnalyzer(Executor, Analyzer)}
+     * to receive images and perform custom processing by implementing the
      * {@link ImageAnalysis.Analyzer#analyze(ImageProxy, int)} function.
      */
     public interface Analyzer {
@@ -428,7 +424,6 @@
     public static final class Defaults implements ConfigProvider<ImageAnalysisConfig> {
         private static final ImageReaderMode DEFAULT_IMAGE_READER_MODE =
                 ImageReaderMode.ACQUIRE_LATEST_IMAGE;
-        private static final Handler DEFAULT_HANDLER = new Handler(Looper.getMainLooper());
         private static final int DEFAULT_IMAGE_QUEUE_DEPTH = 6;
         private static final Size DEFAULT_TARGET_RESOLUTION = new Size(640, 480);
         private static final Size DEFAULT_MAX_RESOLUTION = new Size(1920, 1080);
@@ -440,9 +435,8 @@
             ImageAnalysisConfig.Builder builder =
                     new ImageAnalysisConfig.Builder()
                             .setImageReaderMode(DEFAULT_IMAGE_READER_MODE)
-                            .setCallbackHandler(DEFAULT_HANDLER)
                             .setImageQueueDepth(DEFAULT_IMAGE_QUEUE_DEPTH)
-                            .setTargetResolution(DEFAULT_TARGET_RESOLUTION)
+                            .setDefaultResolution(DEFAULT_TARGET_RESOLUTION)
                             .setMaxResolution(DEFAULT_MAX_RESOLUTION)
                             .setSurfaceOccupancyPriority(DEFAULT_SURFACE_OCCUPANCY_PRIORITY);
 
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysisAbstractAnalyzer.java b/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysisAbstractAnalyzer.java
index 74b18c4..6d1a3c3 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysisAbstractAnalyzer.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysisAbstractAnalyzer.java
@@ -16,8 +16,7 @@
 
 package androidx.camera.core;
 
-import android.os.Handler;
-
+import java.util.concurrent.Executor;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicReference;
@@ -34,16 +33,16 @@
     // Member variables from ImageAnalysis.
     private final AtomicReference<ImageAnalysis.Analyzer> mSubscribedAnalyzer;
     private final AtomicInteger mRelativeRotation;
-    final Handler mUserHandler;
+    final AtomicReference<Executor> mUserExecutor;
 
     // Flag that reflects the state of ImageAnalysis.
     private AtomicBoolean mIsClosed;
 
     ImageAnalysisAbstractAnalyzer(AtomicReference<ImageAnalysis.Analyzer> subscribedAnalyzer,
-            AtomicInteger relativeRotation, Handler userHandler) {
+            AtomicInteger relativeRotation, AtomicReference<Executor> userExecutor) {
         mSubscribedAnalyzer = subscribedAnalyzer;
         mRelativeRotation = relativeRotation;
-        mUserHandler = userHandler;
+        mUserExecutor = userExecutor;
         mIsClosed = new AtomicBoolean(false);
     }
 
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysisBlockingAnalyzer.java b/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysisBlockingAnalyzer.java
index 1283f8d..43e3a65 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysisBlockingAnalyzer.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysisBlockingAnalyzer.java
@@ -16,8 +16,7 @@
 
 package androidx.camera.core;
 
-import android.os.Handler;
-
+import java.util.concurrent.Executor;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicReference;
 
@@ -30,8 +29,8 @@
 
     ImageAnalysisBlockingAnalyzer(
             AtomicReference<ImageAnalysis.Analyzer> subscribedAnalyzer,
-            AtomicInteger relativeRotation, Handler userHandler) {
-        super(subscribedAnalyzer, relativeRotation, userHandler);
+            AtomicInteger relativeRotation, AtomicReference<Executor> userExecutor) {
+        super(subscribedAnalyzer, relativeRotation, userExecutor);
     }
 
     @Override
@@ -40,18 +39,23 @@
         if (image == null) {
             return;
         }
-        try {
-            mUserHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    try {
-                        analyzeImage(image);
-                    } finally {
-                        image.close();
+        Executor executor = mUserExecutor.get();
+        if (executor != null) {
+            try {
+                executor.execute(new Runnable() {
+                    @Override
+                    public void run() {
+                        try {
+                            analyzeImage(image);
+                        } finally {
+                            image.close();
+                        }
                     }
-                }
-            });
-        } catch (RuntimeException e) {
+                });
+            } catch (RuntimeException e) {
+                image.close();
+            }
+        } else {
             image.close();
         }
     }
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysisConfig.java b/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysisConfig.java
index 6018d40..310952c 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysisConfig.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysisConfig.java
@@ -17,7 +17,7 @@
 package androidx.camera.core;
 
 import android.media.ImageReader;
-import android.os.Handler;
+import android.util.Pair;
 import android.util.Rational;
 import android.util.Size;
 import android.view.Surface;
@@ -28,6 +28,7 @@
 import androidx.annotation.RestrictTo.Scope;
 import androidx.camera.core.ImageAnalysis.ImageReaderMode;
 
+import java.util.List;
 import java.util.Set;
 import java.util.UUID;
 import java.util.concurrent.Executor;
@@ -284,11 +285,13 @@
      * @param valueIfMissing The value to return if this configuration option has not been set.
      * @return The stored value or <code>valueIfMissing</code> if the value does not exist in this
      * configuration.
+     * @hide
      */
+    @RestrictTo(Scope.LIBRARY_GROUP)
     @Override
     @Nullable
-    public Rational getTargetAspectRatio(@Nullable Rational valueIfMissing) {
-        return retrieveOption(OPTION_TARGET_ASPECT_RATIO, valueIfMissing);
+    public Rational getTargetAspectRatioCustom(@Nullable Rational valueIfMissing) {
+        return retrieveOption(OPTION_TARGET_ASPECT_RATIO_CUSTOM, valueIfMissing);
     }
 
     /**
@@ -300,10 +303,37 @@
      *
      * @return The stored value, if it exists in this configuration.
      * @throws IllegalArgumentException if the option does not exist in this configuration.
+     * @hide
      */
-    @Override
     @NonNull
-    public Rational getTargetAspectRatio() {
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Override
+    public Rational getTargetAspectRatioCustom() {
+        return retrieveOption(OPTION_TARGET_ASPECT_RATIO_CUSTOM);
+    }
+
+    /**
+     * Retrieves the aspect ratio of the target intending to use images from this configuration.
+     *
+     * @param valueIfMissing The value to return if this configuration option has not been set.
+     * @return The stored value or <code>valueIfMissing</code> if the value does not exist in this
+     * configuration.
+     */
+    @Nullable
+    @Override
+    public AspectRatio getTargetAspectRatio(@Nullable AspectRatio valueIfMissing) {
+        return retrieveOption(OPTION_TARGET_ASPECT_RATIO, valueIfMissing);
+    }
+
+    /**
+     * Retrieves the aspect ratio of the target intending to use images from this configuration.
+     *
+     * @return The stored value, if it exists in this configuration.
+     * @throws IllegalArgumentException if the option does not exist in this configuration.
+     */
+    @NonNull
+    @Override
+    public AspectRatio getTargetAspectRatio() {
         return retrieveOption(OPTION_TARGET_ASPECT_RATIO);
     }
 
@@ -365,6 +395,35 @@
         return retrieveOption(ImageOutputConfig.OPTION_TARGET_RESOLUTION);
     }
 
+    /**
+     * Retrieves the default resolution of the target intending to use from this configuration.
+     *
+     * @param valueIfMissing The value to return if this configuration option has not been set.
+     * @return The stored value or <code>valueIfMissing</code> if the value does not exist in this
+     * configuration.
+     * @hide
+     */
+    @Nullable
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Override
+    public Size getDefaultResolution(@Nullable Size valueIfMissing) {
+        return retrieveOption(ImageOutputConfig.OPTION_DEFAULT_RESOLUTION, valueIfMissing);
+    }
+
+    /**
+     * Retrieves the default resolution of the target intending to use from this configuration.
+     *
+     * @return The stored value, if it exists in this configuration.
+     * @throws IllegalArgumentException if the option does not exist in this configuration.
+     * @hide
+     */
+    @NonNull
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Override
+    public Size getDefaultResolution() {
+        return retrieveOption(ImageOutputConfig.OPTION_DEFAULT_RESOLUTION);
+    }
+
     /** @hide */
     @RestrictTo(Scope.LIBRARY_GROUP)
     @Override
@@ -381,33 +440,25 @@
         return retrieveOption(OPTION_MAX_RESOLUTION);
     }
 
-    // Implementations of ThreadConfig default methods
-
-    /**
-     * Returns the default handler that will be used for callbacks.
-     *
-     * @param valueIfMissing The value to return if this configuration option has not been set.
-     * @return The stored value or <code>valueIfMissing</code> if the value does not exist in this
-     * configuration.
-     */
+    /** @hide */
+    @RestrictTo(Scope.LIBRARY_GROUP)
     @Override
     @Nullable
-    public Handler getCallbackHandler(@Nullable Handler valueIfMissing) {
-        return retrieveOption(OPTION_CALLBACK_HANDLER, valueIfMissing);
+    public List<Pair<Integer, Size[]>> getSupportedResolutions(
+            @Nullable List<Pair<Integer, Size[]>> valueIfMissing) {
+        return retrieveOption(OPTION_SUPPORTED_RESOLUTIONS, valueIfMissing);
     }
 
-    /**
-     * Returns the default handler that will be used for callbacks.
-     *
-     * @return The stored value, if it exists in this configuration.
-     * @throws IllegalArgumentException if the option does not exist in this configuration.
-     */
+    /** @hide */
+    @RestrictTo(Scope.LIBRARY_GROUP)
     @Override
     @NonNull
-    public Handler getCallbackHandler() {
-        return retrieveOption(OPTION_CALLBACK_HANDLER);
+    public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+        return retrieveOption(OPTION_SUPPORTED_RESOLUTIONS);
     }
 
+    // Implementations of ThreadConfig default methods
+
     /**
      * Returns the executor that will be used for background tasks.
      *
@@ -641,6 +692,14 @@
          */
         @NonNull
         public ImageAnalysisConfig build() {
+            // Error at runtime for using both setTargetResolution and setTargetAspectRatio on
+            // the same config.
+            if (getMutableConfig().retrieveOption(OPTION_TARGET_ASPECT_RATIO, null) != null
+                    && getMutableConfig().retrieveOption(OPTION_TARGET_RESOLUTION, null) != null) {
+                throw new IllegalArgumentException(
+                        "Cannot use both setTargetResolution and setTargetAspectRatio on the same"
+                                + " config.");
+            }
             return new ImageAnalysisConfig(OptionsBundle.from(mMutableConfig));
         }
 
@@ -728,16 +787,45 @@
          * ratio which may differ from the request, possibly due to device constraints.
          * Application code should check the resulting output's resolution.
          *
+         * <p>This method can be used to request an aspect ratio that is not from the standard set
+         * of aspect ratios defined in the {@link AspectRatio}.
+         *
+         * <p>This method will remove any value set by setTargetAspectRatio().
+         *
          * <p>For ImageAnalysis, the output is the {@link ImageProxy} passed to the analyzer
          * function.
          *
          * @param aspectRatio A {@link Rational} representing the ratio of the target's width and
          *                    height.
          * @return The current Builder.
+         * @hide
          */
-        @Override
         @NonNull
-        public Builder setTargetAspectRatio(@NonNull Rational aspectRatio) {
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        @Override
+        public Builder setTargetAspectRatioCustom(@NonNull Rational aspectRatio) {
+            getMutableConfig().insertOption(OPTION_TARGET_ASPECT_RATIO_CUSTOM, aspectRatio);
+            getMutableConfig().removeOption(OPTION_TARGET_ASPECT_RATIO);
+            return this;
+        }
+
+        /**
+         * Sets the aspect ratio of the intended target for images from this configuration.
+         *
+         * <p>It is not allowed to set both target aspect ratio and target resolution on the same
+         * use case.
+         *
+         * <p>The target aspect ratio is used as a hint when determining the resulting output aspect
+         * ratio which may differ from the request, possibly due to device constraints.
+         * Application code should check the resulting output's resolution.
+         *
+         * @param aspectRatio A {@link AspectRatio} representing the ratio of the
+         *                    target's width and height.
+         * @return The current Builder.
+         */
+        @NonNull
+        @Override
+        public Builder setTargetAspectRatio(@NonNull AspectRatio aspectRatio) {
             getMutableConfig().insertOption(OPTION_TARGET_ASPECT_RATIO, aspectRatio);
             return this;
         }
@@ -752,8 +840,8 @@
          * @param rotation The rotation of the intended target.
          * @return The current Builder.
          */
-        @Override
         @NonNull
+        @Override
         public Builder setTargetRotation(@RotationValue int rotation) {
             getMutableConfig().insertOption(OPTION_TARGET_ROTATION, rotation);
             return this;
@@ -768,14 +856,49 @@
          * if no resolution exists that is equal to or larger than the target resolution, the
          * nearest available resolution smaller than the target resolution will be chosen.
          *
+         * <p>It is not allowed to set both target aspect ratio and target resolution on the same
+         * use case.
+         *
+         * <p>The target aspect ratio will also be set the same as the aspect ratio of the provided
+         * {@link Size}. Make sure to set the target resolution with the correct orientation.
+         *
          * @param resolution The target resolution to choose from supported output sizes list.
          * @return The current Builder.
          */
-        @Override
         @NonNull
+        @Override
         public Builder setTargetResolution(@NonNull Size resolution) {
             getMutableConfig()
                     .insertOption(ImageOutputConfig.OPTION_TARGET_RESOLUTION, resolution);
+            if (resolution != null) {
+                getMutableConfig().insertOption(OPTION_TARGET_ASPECT_RATIO_CUSTOM,
+                        new Rational(resolution.getWidth(), resolution.getHeight()));
+            }
+            return this;
+        }
+
+        /**
+         * Sets the default resolution of the intended target from this configuration.
+         *
+         * @param resolution The default resolution to choose from supported output sizes list.
+         * @return The current Builder.
+         * @hide
+         */
+        @NonNull
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        @Override
+        public Builder setDefaultResolution(@NonNull Size resolution) {
+            getMutableConfig().insertOption(ImageOutputConfig.OPTION_DEFAULT_RESOLUTION,
+                    resolution);
+            return this;
+        }
+
+        /** @hide */
+        @NonNull
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        @Override
+        public Builder setMaxResolution(@NonNull Size resolution) {
+            getMutableConfig().insertOption(OPTION_MAX_RESOLUTION, resolution);
             return this;
         }
 
@@ -783,27 +906,14 @@
         @RestrictTo(Scope.LIBRARY_GROUP)
         @Override
         @NonNull
-        public Builder setMaxResolution(@NonNull Size resolution) {
-            getMutableConfig().insertOption(OPTION_MAX_RESOLUTION, resolution);
+        public Builder setSupportedResolutions(@NonNull List<Pair<Integer, Size[]>> resolutions) {
+            getMutableConfig().insertOption(OPTION_SUPPORTED_RESOLUTIONS, resolutions);
             return this;
         }
 
         // Implementations of ThreadConfig.Builder default methods
 
         /**
-         * Sets the default handler that will be used for callbacks.
-         *
-         * @param handler The handler which will be used to post callbacks.
-         * @return the current Builder.
-         */
-        @Override
-        @NonNull
-        public Builder setCallbackHandler(@NonNull Handler handler) {
-            getMutableConfig().insertOption(OPTION_CALLBACK_HANDLER, handler);
-            return this;
-        }
-
-        /**
          * Sets the default executor that will be used for background tasks.
          *
          * @param executor The executor which will be used for background tasks.
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysisNonBlockingAnalyzer.java b/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysisNonBlockingAnalyzer.java
index 0583b38..71a6232 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysisNonBlockingAnalyzer.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysisNonBlockingAnalyzer.java
@@ -16,7 +16,6 @@
 
 package androidx.camera.core;
 
-import android.os.Handler;
 import android.util.Log;
 
 import androidx.annotation.GuardedBy;
@@ -52,8 +51,9 @@
     private final AtomicLong mFinishedImageTimestamp;
 
     ImageAnalysisNonBlockingAnalyzer(AtomicReference<ImageAnalysis.Analyzer> subscribedAnalyzer,
-            AtomicInteger relativeRotation, Handler userHandler, Executor executor) {
-        super(subscribedAnalyzer, relativeRotation, userHandler);
+            AtomicInteger relativeRotation, AtomicReference<Executor> userExecutor,
+            Executor executor) {
+        super(subscribedAnalyzer, relativeRotation, userExecutor);
         mBackgroundExecutor = executor;
         mPostedImageTimestamp = new AtomicLong();
         mFinishedImageTimestamp = new AtomicLong();
@@ -128,26 +128,31 @@
         }
 
         mPostedImageTimestamp.set(imageProxy.getTimestamp());
-        try {
-            mUserHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    try {
-                        analyzeImage(imageProxy);
-                    } finally {
-                        finishImage(imageProxy);
-                        mBackgroundExecutor.execute(new Runnable() {
-                            @Override
-                            public void run() {
-                                analyzeCachedImage();
-                            }
-                        });
+        Executor executor = mUserExecutor.get();
+        if (executor != null) {
+            try {
+                executor.execute(new Runnable() {
+                    @Override
+                    public void run() {
+                        try {
+                            analyzeImage(imageProxy);
+                        } finally {
+                            finishImage(imageProxy);
+                            mBackgroundExecutor.execute(new Runnable() {
+                                @Override
+                                public void run() {
+                                    analyzeCachedImage();
+                                }
+                            });
+                        }
                     }
-                }
-            });
-        } catch (RuntimeException e) {
-            // Unblock if fails to post to user thread.
-            Log.e(TAG, "Error calling user callback", e);
+                });
+            } catch (RuntimeException e) {
+                // Unblock if fails to post to user thread.
+                Log.e(TAG, "Error calling user callback", e);
+                finishImage(imageProxy);
+            }
+        } else {
             finishImage(imageProxy);
         }
     }
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ImageCapture.java b/camera/camera-core/src/main/java/androidx/camera/core/ImageCapture.java
index 5e775dbe..dd76501 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/ImageCapture.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ImageCapture.java
@@ -22,6 +22,7 @@
 import android.media.Image;
 import android.media.ImageReader;
 import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.SystemClock;
 import android.util.Log;
@@ -55,12 +56,13 @@
 import com.google.common.util.concurrent.ListenableFuture;
 
 import java.io.File;
-import java.util.ArrayDeque;
 import java.util.ArrayList;
+import java.util.Deque;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.ConcurrentLinkedDeque;
 import java.util.concurrent.Executor;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
@@ -101,10 +103,13 @@
     private static final Metadata EMPTY_METADATA = new Metadata();
     @SuppressWarnings("WeakerAccess") /* synthetic accessor */
     final Handler mMainHandler = new Handler(Looper.getMainLooper());
+    @Nullable
+    private HandlerThread mProcessingImageResultThread;
+    @Nullable
+    private Handler mProcessingImageResultHandler;
+
     @SuppressWarnings("WeakerAccess") /* synthetic accessor */
-    final ArrayDeque<ImageCaptureRequest> mImageCaptureRequests = new ArrayDeque<>();
-    @SuppressWarnings("WeakerAccess") /* synthetic accessor */
-    final Handler mHandler;
+    final Deque<ImageCaptureRequest> mImageCaptureRequests = new ConcurrentLinkedDeque<>();
     @SuppressWarnings("WeakerAccess") /* synthetic accessor */
     SessionConfig.Builder mSessionConfigBuilder;
     private final CaptureConfig mCaptureConfig;
@@ -197,11 +202,6 @@
             mEnableCheck3AConverged = false; // skip 3A convergence in MIN_LATENCY mode
         }
 
-        mHandler = mConfig.getCallbackHandler(null);
-        if (mHandler == null) {
-            throw new IllegalStateException("No default handler specified.");
-        }
-
         CaptureConfig.Builder captureBuilder = CaptureConfig.Builder.createFrom(mConfig);
         mCaptureConfig = captureBuilder.build();
     }
@@ -212,20 +212,27 @@
         SessionConfig.Builder sessionConfigBuilder = SessionConfig.Builder.createFrom(config);
         sessionConfigBuilder.addRepeatingCameraCaptureCallback(mSessionCallbackChecker);
 
+        mProcessingImageResultThread = new HandlerThread("OnImageAvailableHandlerThread");
+        mProcessingImageResultThread.start();
+        mProcessingImageResultHandler = new Handler(mProcessingImageResultThread.getLooper());
+
         // Setup the ImageReader to do processing
         if (mCaptureProcessor != null) {
+            // TODO: To allow user to use an Executor for the image processing.
             ProcessingImageReader processingImageReader =
                     new ProcessingImageReader(
                             resolution.getWidth(),
                             resolution.getHeight(),
                             getImageFormat(), mMaxCaptureStages,
-                            mHandler, getCaptureBundle(CaptureBundles.singleDefaultCaptureBundle()),
+                            mProcessingImageResultHandler,
+                            getCaptureBundle(CaptureBundles.singleDefaultCaptureBundle()),
                             mCaptureProcessor);
             mMetadataMatchingCaptureCallback = processingImageReader.getCameraCaptureCallback();
             mImageReader = processingImageReader;
         } else {
             MetadataImageReader metadataImageReader = new MetadataImageReader(resolution.getWidth(),
-                    resolution.getHeight(), getImageFormat(), MAX_IMAGES, mHandler);
+                    resolution.getHeight(), getImageFormat(), MAX_IMAGES,
+                    mProcessingImageResultHandler);
             mMetadataMatchingCaptureCallback = metadataImageReader.getCameraCaptureCallback();
             mImageReader = metadataImageReader;
         }
@@ -256,7 +263,7 @@
                         }
                     }
                 },
-                mMainHandler);
+                mProcessingImageResultHandler);
 
         mDeferrableSurface = new ImmediateSurface(mImageReader.getSurface());
         sessionConfigBuilder.addNonRepeatingSurface(mDeferrableSurface);
@@ -285,6 +292,7 @@
         mDeferrableSurface = null;
         ImageReaderProxy imageReaderProxy = mImageReader;
         mImageReader = null;
+        HandlerThread handlerThread = mProcessingImageResultThread;
 
         if (deferrableSurface != null) {
             deferrableSurface.setOnSurfaceDetachedListener(
@@ -295,6 +303,9 @@
                             if (imageReaderProxy != null) {
                                 imageReaderProxy.close();
                             }
+
+                            // Close the handlerThread after the ImageReader was closed.
+                            handlerThread.quitSafely();
                         }
                     });
         }
@@ -363,11 +374,11 @@
      *
      * @param aspectRatio New target aspect ratio.
      */
-    public void setTargetAspectRatio(Rational aspectRatio) {
+    public void setTargetAspectRatioCustom(Rational aspectRatio) {
         ImageOutputConfig oldConfig = (ImageOutputConfig) getUseCaseConfig();
-        Rational oldRatio = oldConfig.getTargetAspectRatio(null);
+        Rational oldRatio = oldConfig.getTargetAspectRatioCustom(null);
         if (!aspectRatio.equals(oldRatio)) {
-            mUseCaseConfigBuilder.setTargetAspectRatio(aspectRatio);
+            mUseCaseConfigBuilder.setTargetAspectRatioCustom(aspectRatio);
             updateUseCaseConfig(mUseCaseConfigBuilder.build());
             mConfig = (ImageCaptureConfig) getUseCaseConfig();
 
@@ -577,7 +588,7 @@
             Log.e(TAG, "Unable to retrieve camera sensor orientation.", e);
         }
 
-        Rational targetRatio = mConfig.getTargetAspectRatio(null);
+        Rational targetRatio = mConfig.getTargetAspectRatioCustom(null);
         targetRatio = ImageUtil.rotate(targetRatio, relativeRotation);
 
         mImageCaptureRequests.offer(
@@ -1160,7 +1171,6 @@
             implements ConfigProvider<ImageCaptureConfig> {
         private static final CaptureMode DEFAULT_CAPTURE_MODE = CaptureMode.MIN_LATENCY;
         private static final FlashMode DEFAULT_FLASH_MODE = FlashMode.OFF;
-        private static final Handler DEFAULT_HANDLER = new Handler(Looper.getMainLooper());
         private static final int DEFAULT_SURFACE_OCCUPANCY_PRIORITY = 4;
 
         private static final ImageCaptureConfig DEFAULT_CONFIG;
@@ -1170,7 +1180,6 @@
                     new ImageCaptureConfig.Builder()
                             .setCaptureMode(DEFAULT_CAPTURE_MODE)
                             .setFlashMode(DEFAULT_FLASH_MODE)
-                            .setCallbackHandler(DEFAULT_HANDLER)
                             .setSurfaceOccupancyPriority(DEFAULT_SURFACE_OCCUPANCY_PRIORITY);
 
             DEFAULT_CONFIG = builder.build();
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ImageCaptureConfig.java b/camera/camera-core/src/main/java/androidx/camera/core/ImageCaptureConfig.java
index 140f23d..1192c7e 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/ImageCaptureConfig.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ImageCaptureConfig.java
@@ -17,7 +17,7 @@
 package androidx.camera.core;
 
 import android.graphics.ImageFormat;
-import android.os.Handler;
+import android.util.Pair;
 import android.util.Rational;
 import android.util.Size;
 import android.view.Surface;
@@ -28,6 +28,7 @@
 import androidx.annotation.RestrictTo.Scope;
 import androidx.camera.core.ImageCapture.CaptureMode;
 
+import java.util.List;
 import java.util.Set;
 import java.util.UUID;
 import java.util.concurrent.Executor;
@@ -389,11 +390,13 @@
      * @param valueIfMissing The value to return if this configuration option has not been set.
      * @return The stored value or <code>valueIfMissing</code> if the value does not exist in this
      * configuration.
+     * @hide
      */
+    @RestrictTo(Scope.LIBRARY_GROUP)
     @Override
     @Nullable
-    public Rational getTargetAspectRatio(@Nullable Rational valueIfMissing) {
-        return retrieveOption(OPTION_TARGET_ASPECT_RATIO, valueIfMissing);
+    public Rational getTargetAspectRatioCustom(@Nullable Rational valueIfMissing) {
+        return retrieveOption(OPTION_TARGET_ASPECT_RATIO_CUSTOM, valueIfMissing);
     }
 
     /**
@@ -405,10 +408,37 @@
      *
      * @return The stored value, if it exists in this configuration.
      * @throws IllegalArgumentException if the option does not exist in this configuration.
+     * @hide
      */
-    @Override
     @NonNull
-    public Rational getTargetAspectRatio() {
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Override
+    public Rational getTargetAspectRatioCustom() {
+        return retrieveOption(OPTION_TARGET_ASPECT_RATIO_CUSTOM);
+    }
+
+    /**
+     * Retrieves the aspect ratio of the target intending to use images from this configuration.
+     *
+     * @param valueIfMissing The value to return if this configuration option has not been set.
+     * @return The stored value or <code>valueIfMissing</code> if the value does not exist in this
+     * configuration.
+     */
+    @Nullable
+    @Override
+    public AspectRatio getTargetAspectRatio(@Nullable AspectRatio valueIfMissing) {
+        return retrieveOption(OPTION_TARGET_ASPECT_RATIO, valueIfMissing);
+    }
+
+    /**
+     * Retrieves the aspect ratio of the target intending to use images from this configuration.
+     *
+     * @return The stored value, if it exists in this configuration.
+     * @throws IllegalArgumentException if the option does not exist in this configuration.
+     */
+    @NonNull
+    @Override
+    public AspectRatio getTargetAspectRatio() {
         return retrieveOption(OPTION_TARGET_ASPECT_RATIO);
     }
 
@@ -470,6 +500,35 @@
         return retrieveOption(ImageOutputConfig.OPTION_TARGET_RESOLUTION);
     }
 
+    /**
+     * Retrieves the default resolution of the target intending to use from this configuration.
+     *
+     * @param valueIfMissing The value to return if this configuration option has not been set.
+     * @return The stored value or <code>valueIfMissing</code> if the value does not exist in this
+     * configuration.
+     * @hide
+     */
+    @Nullable
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Override
+    public Size getDefaultResolution(@Nullable Size valueIfMissing) {
+        return retrieveOption(OPTION_DEFAULT_RESOLUTION, valueIfMissing);
+    }
+
+    /**
+     * Retrieves the default resolution of the target intending to use from this configuration.
+     *
+     * @return The stored value, if it exists in this configuration.
+     * @throws IllegalArgumentException if the option does not exist in this configuration.
+     * @hide
+     */
+    @NonNull
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Override
+    public Size getDefaultResolution() {
+        return retrieveOption(OPTION_DEFAULT_RESOLUTION);
+    }
+
     /** @hide */
     @RestrictTo(Scope.LIBRARY_GROUP)
     @Override
@@ -486,33 +545,25 @@
         return retrieveOption(OPTION_MAX_RESOLUTION);
     }
 
-    // Implementations of ThreadConfig default methods
-
-    /**
-     * Returns the default handler that will be used for callbacks.
-     *
-     * @param valueIfMissing The value to return if this configuration option has not been set.
-     * @return The stored value or <code>valueIfMissing</code> if the value does not exist in this
-     * configuration.
-     */
+    /** @hide */
+    @RestrictTo(Scope.LIBRARY_GROUP)
     @Override
     @Nullable
-    public Handler getCallbackHandler(@Nullable Handler valueIfMissing) {
-        return retrieveOption(OPTION_CALLBACK_HANDLER, valueIfMissing);
+    public List<Pair<Integer, Size[]>> getSupportedResolutions(
+            @Nullable List<Pair<Integer, Size[]>> valueIfMissing) {
+        return retrieveOption(OPTION_SUPPORTED_RESOLUTIONS, valueIfMissing);
     }
 
-    /**
-     * Returns the default handler that will be used for callbacks.
-     *
-     * @return The stored value, if it exists in this configuration.
-     * @throws IllegalArgumentException if the option does not exist in this configuration.
-     */
+    /** @hide */
+    @RestrictTo(Scope.LIBRARY_GROUP)
     @Override
     @NonNull
-    public Handler getCallbackHandler() {
-        return retrieveOption(OPTION_CALLBACK_HANDLER);
+    public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+        return retrieveOption(OPTION_SUPPORTED_RESOLUTIONS);
     }
 
+    // Implementations of ThreadConfig default methods
+
     /**
      * Returns the executor that will be used for background tasks.
      *
@@ -699,6 +750,14 @@
          */
         @NonNull
         public ImageCaptureConfig build() {
+            // Error at runtime for using both setTargetResolution and setTargetAspectRatio on
+            // the same config.
+            if (getMutableConfig().retrieveOption(OPTION_TARGET_ASPECT_RATIO, null) != null
+                    && getMutableConfig().retrieveOption(OPTION_TARGET_RESOLUTION, null) != null) {
+                throw new IllegalArgumentException(
+                        "Cannot use both setTargetResolution and setTargetAspectRatio on the same "
+                                + "config.");
+            }
             return new ImageCaptureConfig(OptionsBundle.from(mMutableConfig));
         }
 
@@ -793,6 +852,15 @@
             return this;
         }
 
+        /** @hide */
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        @Override
+        @NonNull
+        public Builder setSupportedResolutions(@NonNull List<Pair<Integer, Size[]>> resolutions) {
+            getMutableConfig().insertOption(OPTION_SUPPORTED_RESOLUTIONS, resolutions);
+            return this;
+        }
+
         // Implementations of TargetConfig.Builder default methods
 
         /** @hide */
@@ -877,6 +945,11 @@
          * ratio which may differ from the request, possibly due to device constraints.
          * Application code should check the resulting output's resolution.
          *
+         * <p>This method can be used to request an aspect ratio that is not from the standard set
+         * of aspect ratios defined in the {@link AspectRatio}.
+         *
+         * <p>This method will remove any value set by setTargetAspectRatio().
+         *
          * <p>For ImageCapture, the outputs are the {@link ImageProxy} or the File passed to image
          * capture listeners.
          *
@@ -884,9 +957,31 @@
          *                    height.
          * @return The current Builder.
          */
-        @Override
         @NonNull
-        public Builder setTargetAspectRatio(@NonNull Rational aspectRatio) {
+        @Override
+        public Builder setTargetAspectRatioCustom(@NonNull Rational aspectRatio) {
+            getMutableConfig().insertOption(OPTION_TARGET_ASPECT_RATIO_CUSTOM, aspectRatio);
+            getMutableConfig().removeOption(OPTION_TARGET_ASPECT_RATIO);
+            return this;
+        }
+
+        /**
+         * Sets the aspect ratio of the intended target for images from this configuration.
+         *
+         * <p>It is not allowed to set both target aspect ratio and target resolution on the same
+         * use case.
+         *
+         * <p>The target aspect ratio is used as a hint when determining the resulting output aspect
+         * ratio which may differ from the request, possibly due to device constraints.
+         * Application code should check the resulting output's resolution.
+         *
+         * @param aspectRatio A {@link AspectRatio} representing the ratio of the
+         *                    target's width and height.
+         * @return The current Builder.
+         */
+        @NonNull
+        @Override
+        public Builder setTargetAspectRatio(@NonNull AspectRatio aspectRatio) {
             getMutableConfig().insertOption(OPTION_TARGET_ASPECT_RATIO, aspectRatio);
             return this;
         }
@@ -901,8 +996,8 @@
          * @param rotation The rotation of the intended target.
          * @return The current Builder.
          */
-        @Override
         @NonNull
+        @Override
         public Builder setTargetRotation(@RotationValue int rotation) {
             getMutableConfig().insertOption(OPTION_TARGET_ROTATION, rotation);
             return this;
@@ -917,20 +1012,46 @@
          * if no resolution exists that is equal to or larger than the target resolution, the
          * nearest available resolution smaller than the target resolution will be chosen.
          *
+         * <p>It is not allowed to set both target aspect ratio and target resolution on the same
+         * use case.
+         *
+         * <p>The target aspect ratio will also be set the same as the aspect ratio of the provided
+         * {@link Size}. Make sure to set the target resolution with the correct orientation.
+         *
          * @param resolution The target resolution to choose from supported output sizes list.
          * @return The current Builder.
          */
-        @Override
         @NonNull
+        @Override
         public Builder setTargetResolution(@NonNull Size resolution) {
             getMutableConfig().insertOption(OPTION_TARGET_RESOLUTION, resolution);
+            if (resolution != null) {
+                getMutableConfig().insertOption(OPTION_TARGET_ASPECT_RATIO_CUSTOM,
+                        new Rational(resolution.getWidth(), resolution.getHeight()));
+            }
+            return this;
+        }
+
+        /**
+         * Sets the default resolution of the intended target from this configuration.
+         *
+         * @param resolution The default resolution to choose from supported output sizes list.
+         * @return The current Builder.
+         * @hide
+         */
+        @NonNull
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        @Override
+        public Builder setDefaultResolution(@NonNull Size resolution) {
+            getMutableConfig().insertOption(ImageOutputConfig.OPTION_DEFAULT_RESOLUTION,
+                    resolution);
             return this;
         }
 
         /** @hide */
+        @NonNull
         @RestrictTo(Scope.LIBRARY_GROUP)
         @Override
-        @NonNull
         public Builder setMaxResolution(@NonNull Size resolution) {
             getMutableConfig().insertOption(OPTION_MAX_RESOLUTION, resolution);
             return this;
@@ -939,19 +1060,6 @@
         // Implementations of ThreadConfig.Builder default methods
 
         /**
-         * Sets the default handler that will be used for callbacks.
-         *
-         * @param handler The handler which will be used to post callbacks.
-         * @return the current Builder.
-         */
-        @Override
-        @NonNull
-        public Builder setCallbackHandler(@NonNull Handler handler) {
-            getMutableConfig().insertOption(OPTION_CALLBACK_HANDLER, handler);
-            return this;
-        }
-
-        /**
          * Sets the default executor that will be used for background tasks.
          *
          * @param executor The executor which will be used for background tasks.
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ImageOutputConfig.java b/camera/camera-core/src/main/java/androidx/camera/core/ImageOutputConfig.java
index 9e5f150..9067ee0 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/ImageOutputConfig.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ImageOutputConfig.java
@@ -16,6 +16,8 @@
 
 package androidx.camera.core;
 
+import android.graphics.ImageFormat;
+import android.util.Pair;
 import android.util.Rational;
 import android.util.Size;
 import android.view.Surface;
@@ -29,6 +31,7 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.List;
 
 /**
  * Configuration containing options for configuring the output image data of a pipeline.
@@ -49,13 +52,23 @@
     // *********************************************************************************************
 
     /**
+     * Option: camerax.core.imageOutput.targetAspectRatioCustom
+     *
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    Option<Rational> OPTION_TARGET_ASPECT_RATIO_CUSTOM =
+            Option.create("camerax.core.imageOutput.targetAspectRatioCustom", Rational.class);
+
+    /**
      * Option: camerax.core.imageOutput.targetAspectRatio
      *
      * @hide
      */
     @RestrictTo(Scope.LIBRARY_GROUP)
-    Option<Rational> OPTION_TARGET_ASPECT_RATIO =
-            Option.create("camerax.core.imageOutput.targetAspectRatio", Rational.class);
+    Option<AspectRatio> OPTION_TARGET_ASPECT_RATIO =
+            Option.create("camerax.core.imageOutput.targetAspectRatio", AspectRatio.class);
+
     /**
      * Option: camerax.core.imageOutput.targetRotation
      *
@@ -73,6 +86,14 @@
     Option<Size> OPTION_TARGET_RESOLUTION =
             Option.create("camerax.core.imageOutput.targetResolution", Size.class);
     /**
+     * Option: camerax.core.imageOutput.defaultResolution
+     *
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    Option<Size> OPTION_DEFAULT_RESOLUTION =
+            Option.create("camerax.core.imageOutput.defaultResolution", Size.class);
+    /**
      * Option: camerax.core.imageOutput.maxResolution
      *
      * @hide
@@ -80,6 +101,14 @@
     @RestrictTo(Scope.LIBRARY_GROUP)
     Option<Size> OPTION_MAX_RESOLUTION =
             Option.create("camerax.core.imageOutput.maxResolution", Size.class);
+    /**
+     * Option: camerax.core.imageOutput.supportedResolutions
+     *
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    Option<List<Pair<Integer, Size[]>>> OPTION_SUPPORTED_RESOLUTIONS =
+            Option.create("camerax.core.imageOutput.supportedResolutions", List.class);
 
     // *********************************************************************************************
 
@@ -93,9 +122,11 @@
      * @param valueIfMissing The value to return if this configuration option has not been set.
      * @return The stored value or <code>valueIfMissing</code> if the value does not exist in this
      * configuration.
+     * @hide
      */
     @Nullable
-    Rational getTargetAspectRatio(@Nullable Rational valueIfMissing);
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    Rational getTargetAspectRatioCustom(@Nullable Rational valueIfMissing);
 
     /**
      * Retrieves the aspect ratio of the target intending to use images from this configuration.
@@ -106,9 +137,30 @@
      *
      * @return The stored value, if it exists in this configuration.
      * @throws IllegalArgumentException if the option does not exist in this configuration.
+     * @hide
      */
     @NonNull
-    Rational getTargetAspectRatio();
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    Rational getTargetAspectRatioCustom();
+
+    /**
+     * Retrieves the aspect ratio of the target intending to use images from this configuration.
+     *
+     * @param valueIfMissing The value to return if this configuration option has not been set.
+     * @return The stored value or <code>valueIfMissing</code> if the value does not exist in this
+     * configuration.
+     */
+    @Nullable
+    AspectRatio getTargetAspectRatio(@Nullable AspectRatio valueIfMissing);
+
+    /**
+     * Retrieves the aspect ratio of the target intending to use images from this configuration.
+     *
+     * @return The stored value, if it exists in this configuration.
+     * @throws IllegalArgumentException if the option does not exist in this configuration.
+     */
+    @NonNull
+    AspectRatio getTargetAspectRatio();
 
     /**
      * Retrieves the rotation of the target intending to use images from this configuration.
@@ -161,6 +213,29 @@
     Size getTargetResolution();
 
     /**
+     * Retrieves the default resolution of the target intending to use from this configuration.
+     *
+     * @param valueIfMissing The value to return if this configuration option has not been set.
+     * @return The stored value or <code>valueIfMissing</code> if the value does not exist in this
+     * configuration.
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    Size getDefaultResolution(@Nullable Size valueIfMissing);
+
+    /**
+     * Retrieves the default resolution of the target intending to use from this configuration.
+     *
+     * @return The stored value, if it exists in this configuration.
+     * @throws IllegalArgumentException if the option does not exist in this configuration.
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @NonNull
+    Size getDefaultResolution();
+
+    /**
      * Retrieves the max resolution limitation of the target intending to use from this
      * configuration.
      *
@@ -186,6 +261,38 @@
     Size getMaxResolution();
 
     /**
+     * Retrieves the supported resolutions can be used by the target from this configuration.
+     *
+     * <p>Pair list is composed with {@link ImageFormat} and {@link Size} array. The returned
+     * {@link Size} array should be subset of the complete supported sizes list for the camera
+     * device.
+     *
+     * @param valueIfMissing The value to return if this configuration option has not been set.
+     * @return The stored value or <code>valueIfMissing</code> if the value does not exist in this
+     * configuration.
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    List<Pair<Integer, Size[]>> getSupportedResolutions(
+            @Nullable List<Pair<Integer, Size[]>> valueIfMissing);
+
+    /**
+     * Retrieves the supported resolutions can be used by the target from this configuration.
+     *
+     * <p>Pair list is composed with {@link ImageFormat} and {@link Size} array. The returned
+     * {@link Size} array should be subset of the complete supported sizes list for the camera
+     * device.
+     *
+     * @return The stored value, if it exists in this configuration.
+     * @throws IllegalArgumentException if the option does not exist in this configuration.
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @NonNull
+    List<Pair<Integer, Size[]>> getSupportedResolutions();
+
+    /**
      * Builder for a {@link ImageOutputConfig}.
      *
      * @param <B> The top level builder type for which this builder is composed with.
@@ -201,12 +308,32 @@
          * the provided {@link Rational} corresponds to the width, and the denominator corresponds
          * to the height.
          *
+         * <p>This method can be used to request an aspect ratio that is not from the standard set
+         * of aspect ratios defined in the {@link AspectRatio}.
+         *
+         * <p>This method will remove any value set by setTargetAspectRatio().
+         *
          * @param aspectRatio A {@link Rational} representing the ratio of the target's width and
          *                    height.
          * @return The current Builder.
+         * @hide
+         */
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        @NonNull
+        B setTargetAspectRatioCustom(@NonNull Rational aspectRatio);
+
+        /**
+         * Sets the aspect ratio of the intended target for images from this configuration.
+         *
+         * <p>It is not allowed to set both target aspect ratio and target resolution on the same
+         * use case.
+         *
+         * @param aspectRatio A {@link AspectRatio} representing the ratio of the
+         *                    target's width and height.
+         * @return The current Builder.
          */
         @NonNull
-        B setTargetAspectRatio(@NonNull Rational aspectRatio);
+        B setTargetAspectRatio(@NonNull AspectRatio aspectRatio);
 
         /**
          * Sets the rotation of the intended target for images from this configuration.
@@ -224,6 +351,12 @@
         /**
          * Sets the resolution of the intended target from this configuration.
          *
+         * <p>It is not allowed to set both target aspect ratio and target resolution on the same
+         * use case.
+         *
+         * <p>The target aspect ratio will also be set the same as the aspect ratio of the provided
+         * {@link Size}. Make sure to set the target resolution with the correct orientation.
+         *
          * @param resolution The target resolution to choose from supported output sizes list.
          * @return The current Builder.
          * @hide
@@ -233,6 +366,17 @@
         B setTargetResolution(@NonNull Size resolution);
 
         /**
+         * Sets the default resolution of the intended target from this configuration.
+         *
+         * @param resolution The default resolution to choose from supported output sizes list.
+         * @return The current Builder.
+         * @hide
+         */
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        @NonNull
+        B setDefaultResolution(@NonNull Size resolution);
+
+        /**
          * Sets the max resolution limitation of the intended target from this configuration.
          *
          * @param resolution The max resolution limitation to choose from supported output sizes
@@ -243,6 +387,21 @@
         @RestrictTo(Scope.LIBRARY_GROUP)
         @NonNull
         B setMaxResolution(@NonNull Size resolution);
+
+        /**
+         * Sets the supported resolutions can be used by target from this configuration.
+         *
+         * <p>Pair list is composed with {@link ImageFormat} and {@link Size} array. The
+         * {@link Size} array should be subset of the complete supported sizes list for the camera
+         * device.
+         *
+         * @param resolutionsList The resolutions can be supported for image formats.
+         * @return The current Builder.
+         * @hide
+         */
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        @NonNull
+        B setSupportedResolutions(@NonNull List<Pair<Integer, Size[]>> resolutionsList);
     }
 
     /**
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/Preview.java b/camera/camera-core/src/main/java/androidx/camera/core/Preview.java
index 7296f54..82bce2f 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/Preview.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/Preview.java
@@ -20,7 +20,7 @@
 import android.graphics.Rect;
 import android.graphics.SurfaceTexture;
 import android.os.Handler;
-import android.os.Looper;
+import android.os.HandlerThread;
 import android.util.Log;
 import android.util.Rational;
 import android.util.Size;
@@ -37,6 +37,7 @@
 import androidx.camera.core.CameraX.LensFacing;
 import androidx.camera.core.ImageOutputConfig.RotationValue;
 import androidx.camera.core.impl.utils.Threads;
+import androidx.camera.core.impl.utils.executor.CameraXExecutors;
 import androidx.core.util.Preconditions;
 
 import com.google.auto.value.AutoValue;
@@ -44,6 +45,8 @@
 
 import java.util.Map;
 import java.util.Objects;
+import java.util.concurrent.Executor;
+import java.util.concurrent.RejectedExecutionException;
 
 /**
  * A use case that provides a camera preview stream for displaying on-screen.
@@ -82,7 +85,10 @@
     private static final String TAG = "Preview";
     private static final String CONFLICTING_SURFACE_API_ERROR_MESSAGE =
             "PreviewSurfaceCallback cannot be used with OnPreviewOutputUpdateListener.";
-    private final Handler mMainHandler = new Handler(Looper.getMainLooper());
+    @Nullable
+    private HandlerThread mProcessingPreviewThread;
+    @Nullable
+    private Handler mProcessingPreviewHandler;
 
     private final PreviewConfig.Builder mUseCaseConfigBuilder;
     @Nullable
@@ -97,6 +103,8 @@
     @SuppressWarnings("WeakerAccess") /* synthetic accessor */
     SurfaceTextureHolder mSurfaceTextureHolder;
 
+    private Executor mOutputUpdateExecutor;
+
     /**
      * Creates a new preview use case from the given configuration.
      *
@@ -116,13 +124,20 @@
         final CaptureProcessor captureProcessor = config.getCaptureProcessor(null);
         if (captureProcessor != null) {
             CaptureStage captureStage = new CaptureStage.DefaultCaptureStage();
+            // TODO: To allow user to use an Executor for the processing.
+
+            if (mProcessingPreviewHandler == null) {
+                mProcessingPreviewThread = new HandlerThread("ProcessingSurfaceTexture");
+                mProcessingPreviewThread.start();
+                mProcessingPreviewHandler = new Handler(mProcessingPreviewThread.getLooper());
+            }
 
             ProcessingSurfaceTexture processingSurfaceTexture =
                     new ProcessingSurfaceTexture(
                             resolution.getWidth(),
                             resolution.getHeight(),
                             ImageFormat.YUV_420_888,
-                            config.getCallbackHandler(),
+                            mProcessingPreviewHandler,
                             captureStage,
                             captureProcessor);
 
@@ -182,19 +197,31 @@
         if (surfaceTextureHolder != null) {
             surfaceTextureHolder.release();
         }
+
+        // The handler no longer used by the ProcessingSurfaceTexture, we can close the
+        // handlerTread after the ProcessingSurfaceTexture was released.
+        if (mProcessingPreviewHandler != null) {
+            mProcessingPreviewThread.quitSafely();
+            mProcessingPreviewThread = null;
+            mProcessingPreviewHandler = null;
+        }
     }
 
     /**
-     * Removes previously PreviewOutput listener.
-     *
-     * <p>This is equivalent to calling {@code setOnPreviewOutputUpdateListener(null)}.
+     * Un-register a listener previously registered via
+     * {@link Preview#setOnPreviewOutputUpdateListener(OnPreviewOutputUpdateListener)}.
+     * It will signal to the camera that the camera should no longer stream data to the last
+     * {@link PreviewOutput}.
      *
      * @throws IllegalStateException If not called on main thread.
      */
     @UiThread
     public void removePreviewOutputListener() {
         Threads.checkMainThread();
-        setOnPreviewOutputUpdateListener(null);
+        if (mSubscribedPreviewOutputListener != null) {
+            mSubscribedPreviewOutputListener = null;
+            notifyInactive();
+        }
     }
 
     /**
@@ -214,8 +241,7 @@
      * Sets a listener to get the {@link PreviewOutput} updates.
      *
      * <p>Setting this listener will signal to the camera that the use case is ready to receive
-     * data. Setting the listener to {@code null} will signal to the camera that the camera should
-     * no longer stream data to the last {@link PreviewOutput}.
+     * data.
      *
      * <p>Once {@link OnPreviewOutputUpdateListener#onUpdated(PreviewOutput)}  is called,
      * ownership of the {@link PreviewOutput} and its contents is transferred to the application. It
@@ -239,25 +265,44 @@
      * with the output from the previous {@link PreviewOutput} to attach it to a new TextureView,
      * such as on resuming the application.
      *
+     * <p>The listener will run on the UI thread. See
+     * {@link Preview#setOnPreviewOutputUpdateListener(Executor, OnPreviewOutputUpdateListener)}
+     * to set the updates run on the given executor.
+     *
+     * @param newListener The listener which will receive {@link PreviewOutput} updates.
+     */
+    @UiThread
+    public void setOnPreviewOutputUpdateListener(
+            @NonNull OnPreviewOutputUpdateListener newListener) {
+        setOnPreviewOutputUpdateListener(CameraXExecutors.mainThreadExecutor(), newListener);
+    }
+
+    /**
+     * Sets a listener and its executor to get the {@link PreviewOutput} updates.
+     *
+     * <p>See {@link Preview#setOnPreviewOutputUpdateListener(OnPreviewOutputUpdateListener)} for
+     * more information.
+     *
+     * @param executor    The executor on which the listener should be invoked.
      * @param newListener The listener which will receive {@link PreviewOutput} updates.
      * @throws IllegalStateException If not called on main thread.
      */
     @UiThread
     public void setOnPreviewOutputUpdateListener(
-            @Nullable OnPreviewOutputUpdateListener newListener) {
+            @NonNull /* @CallbackExecutor */ Executor executor,
+            @NonNull OnPreviewOutputUpdateListener newListener) {
         Threads.checkMainThread();
         Preconditions.checkState(mPreviewSurfaceCallback == null,
                 CONFLICTING_SURFACE_API_ERROR_MESSAGE);
+        mOutputUpdateExecutor = executor;
         OnPreviewOutputUpdateListener oldListener = mSubscribedPreviewOutputListener;
         mSubscribedPreviewOutputListener = newListener;
         if (oldListener == null && newListener != null) {
             notifyActive();
             if (mLatestPreviewOutput != null) {
                 mSurfaceDispatched = true;
-                newListener.onUpdated(mLatestPreviewOutput);
+                updateListener(newListener, mLatestPreviewOutput);
             }
-        } else if (oldListener != null && newListener == null) {
-            notifyInactive();
         } else if (oldListener != null && oldListener != newListener) {
             if (mLatestPreviewOutput != null) {
                 PreviewConfig config = (PreviewConfig) getUseCaseConfig();
@@ -318,6 +363,14 @@
         updateOutput(mSurfaceTextureHolder.getSurfaceTexture(), resolution);
     }
 
+    private void updateListener(OnPreviewOutputUpdateListener listener, PreviewOutput output) {
+        try {
+            mOutputUpdateExecutor.execute(() -> listener.onUpdated(output));
+        } catch (RejectedExecutionException e) {
+            Log.e(TAG, "Unable to post to the supplied executor.", e);
+        }
+    }
+
     private CameraControlInternal getCurrentCameraControl() {
         PreviewConfig config = (PreviewConfig) getUseCaseConfig();
         String cameraId = getCameraIdUnchecked(config);
@@ -423,7 +476,7 @@
         if (CameraX.getSurfaceManager().requiresCorrectedAspectRatio(config)) {
             Rational resultRatio = CameraX.getSurfaceManager().getCorrectedAspectRatio(config);
             PreviewConfig.Builder configBuilder = PreviewConfig.Builder.fromConfig(config);
-            configBuilder.setTargetAspectRatio(resultRatio);
+            configBuilder.setTargetAspectRatioCustom(resultRatio);
             config = configBuilder.build();
         }
         super.updateUseCaseConfig(config);
@@ -530,7 +583,7 @@
 
             if (outputListener != null) {
                 mSurfaceDispatched = true;
-                outputListener.onUpdated(newOutput);
+                updateListener(outputListener, newOutput);
             }
         }
     }
@@ -584,7 +637,6 @@
      */
     @RestrictTo(Scope.LIBRARY_GROUP)
     public static final class Defaults implements ConfigProvider<PreviewConfig> {
-        private static final Handler DEFAULT_HANDLER = new Handler(Looper.getMainLooper());
         private static final Size DEFAULT_MAX_RESOLUTION =
                 CameraX.getSurfaceManager().getPreviewSize();
         private static final int DEFAULT_SURFACE_OCCUPANCY_PRIORITY = 2;
@@ -594,7 +646,6 @@
         static {
             PreviewConfig.Builder builder =
                     new PreviewConfig.Builder()
-                            .setCallbackHandler(DEFAULT_HANDLER)
                             .setMaxResolution(DEFAULT_MAX_RESOLUTION)
                             .setSurfaceOccupancyPriority(DEFAULT_SURFACE_OCCUPANCY_PRIORITY);
             DEFAULT_CONFIG = builder.build();
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/PreviewConfig.java b/camera/camera-core/src/main/java/androidx/camera/core/PreviewConfig.java
index 8ced57d..4f70179 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/PreviewConfig.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/PreviewConfig.java
@@ -16,7 +16,7 @@
 
 package androidx.camera.core;
 
-import android.os.Handler;
+import android.util.Pair;
 import android.util.Rational;
 import android.util.Size;
 import android.view.Surface;
@@ -26,6 +26,7 @@
 import androidx.annotation.RestrictTo;
 import androidx.annotation.RestrictTo.Scope;
 
+import java.util.List;
 import java.util.Set;
 import java.util.UUID;
 import java.util.concurrent.Executor;
@@ -225,11 +226,13 @@
      * @param valueIfMissing The value to return if this configuration option has not been set.
      * @return The stored value or <code>valueIfMissing</code> if the value does not exist in this
      * configuration.
+     * @hide
      */
+    @RestrictTo(Scope.LIBRARY_GROUP)
     @Override
     @Nullable
-    public Rational getTargetAspectRatio(@Nullable Rational valueIfMissing) {
-        return retrieveOption(OPTION_TARGET_ASPECT_RATIO, valueIfMissing);
+    public Rational getTargetAspectRatioCustom(@Nullable Rational valueIfMissing) {
+        return retrieveOption(OPTION_TARGET_ASPECT_RATIO_CUSTOM, valueIfMissing);
     }
 
     /**
@@ -241,10 +244,37 @@
      *
      * @return The stored value, if it exists in this configuration.
      * @throws IllegalArgumentException if the option does not exist in this configuration.
+     * @hide
      */
-    @Override
+    @RestrictTo(Scope.LIBRARY_GROUP)
     @NonNull
-    public Rational getTargetAspectRatio() {
+    @Override
+    public Rational getTargetAspectRatioCustom() {
+        return retrieveOption(OPTION_TARGET_ASPECT_RATIO_CUSTOM);
+    }
+
+    /**
+     * Retrieves the aspect ratio of the target intending to use images from this configuration.
+     *
+     * @param valueIfMissing The value to return if this configuration option has not been set.
+     * @return The stored value or <code>valueIfMissing</code> if the value does not exist in this
+     * configuration.
+     */
+    @Nullable
+    @Override
+    public AspectRatio getTargetAspectRatio(@Nullable AspectRatio valueIfMissing) {
+        return retrieveOption(OPTION_TARGET_ASPECT_RATIO, valueIfMissing);
+    }
+
+    /**
+     * Retrieves the aspect ratio of the target intending to use images from this configuration.
+     *
+     * @return The stored value, if it exists in this configuration.
+     * @throws IllegalArgumentException if the option does not exist in this configuration.
+     */
+    @NonNull
+    @Override
+    public AspectRatio getTargetAspectRatio() {
         return retrieveOption(OPTION_TARGET_ASPECT_RATIO);
     }
 
@@ -306,6 +336,35 @@
         return retrieveOption(ImageOutputConfig.OPTION_TARGET_RESOLUTION);
     }
 
+    /**
+     * Retrieves the default resolution of the target intending to use from this configuration.
+     *
+     * @param valueIfMissing The value to return if this configuration option has not been set.
+     * @return The stored value or <code>valueIfMissing</code> if the value does not exist in this
+     * configuration.
+     * @hide
+     */
+    @Nullable
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Override
+    public Size getDefaultResolution(@Nullable Size valueIfMissing) {
+        return retrieveOption(ImageOutputConfig.OPTION_DEFAULT_RESOLUTION, valueIfMissing);
+    }
+
+    /**
+     * Retrieves the default resolution of the target intending to use from this configuration.
+     *
+     * @return The stored value, if it exists in this configuration.
+     * @throws IllegalArgumentException if the option does not exist in this configuration.
+     * @hide
+     */
+    @NonNull
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Override
+    public Size getDefaultResolution() {
+        return retrieveOption(ImageOutputConfig.OPTION_DEFAULT_RESOLUTION);
+    }
+
     /** @hide */
     @RestrictTo(Scope.LIBRARY_GROUP)
     @Override
@@ -322,33 +381,25 @@
         return retrieveOption(OPTION_MAX_RESOLUTION);
     }
 
-    // Implementations of ThreadConfig default methods
-
-    /**
-     * Returns the default handler that will be used for callbacks.
-     *
-     * @param valueIfMissing The value to return if this configuration option has not been set.
-     * @return The stored value or <code>valueIfMissing</code> if the value does not exist in this
-     * configuration.
-     */
+    /** @hide */
+    @RestrictTo(Scope.LIBRARY_GROUP)
     @Override
     @Nullable
-    public Handler getCallbackHandler(@Nullable Handler valueIfMissing) {
-        return retrieveOption(OPTION_CALLBACK_HANDLER, valueIfMissing);
+    public List<Pair<Integer, Size[]>> getSupportedResolutions(
+            @Nullable List<Pair<Integer, Size[]>> valueIfMissing) {
+        return retrieveOption(OPTION_SUPPORTED_RESOLUTIONS, valueIfMissing);
     }
 
-    /**
-     * Returns the default handler that will be used for callbacks.
-     *
-     * @return The stored value, if it exists in this configuration.
-     * @throws IllegalArgumentException if the option does not exist in this configuration.
-     */
+    /** @hide */
+    @RestrictTo(Scope.LIBRARY_GROUP)
     @Override
     @NonNull
-    public Handler getCallbackHandler() {
-        return retrieveOption(OPTION_CALLBACK_HANDLER);
+    public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+        return retrieveOption(OPTION_SUPPORTED_RESOLUTIONS);
     }
 
+    // Implementations of ThreadConfig default methods
+
     /**
      * Returns the executor that will be used for background tasks.
      *
@@ -575,6 +626,14 @@
          */
         @NonNull
         public PreviewConfig build() {
+            // Error at runtime for using both setTargetResolution and setTargetAspectRatio on
+            // the same config.
+            if (getMutableConfig().retrieveOption(OPTION_TARGET_ASPECT_RATIO, null) != null
+                    && getMutableConfig().retrieveOption(OPTION_TARGET_RESOLUTION, null) != null) {
+                throw new IllegalArgumentException(
+                        "Cannot use both setTargetResolution and setTargetAspectRatio on the same "
+                                + "config.");
+            }
             return new PreviewConfig(OptionsBundle.from(mMutableConfig));
         }
 
@@ -662,16 +721,48 @@
          * ratio which may differ from the request, possibly due to device constraints.
          * Application code should check the resulting output's resolution.
          *
+         * <p>This method can be used to request an aspect ratio that is not from the standard set
+         * of aspect ratios defined in the {@link AspectRatio}.
+         *
+         * <p>This method will remove any value set by setTargetAspectRatio().
+         *
          * <p>For Preview, the output is the SurfaceTexture of the
          * {@link androidx.camera.core.Preview.PreviewOutput}.
          *
          * @param aspectRatio A {@link Rational} representing the ratio of the target's width and
          *                    height.
          * @return The current Builder.
+         * @hide
          */
-        @Override
         @NonNull
-        public Builder setTargetAspectRatio(@NonNull Rational aspectRatio) {
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        @Override
+        public Builder setTargetAspectRatioCustom(@NonNull Rational aspectRatio) {
+            getMutableConfig().insertOption(OPTION_TARGET_ASPECT_RATIO_CUSTOM, aspectRatio);
+            getMutableConfig().removeOption(OPTION_TARGET_ASPECT_RATIO);
+            return this;
+        }
+
+        /**
+         * Sets the aspect ratio of the intended target for images from this configuration.
+         *
+         * <p>It is not allowed to set both target aspect ratio and target resolution on the same
+         * use case.
+         *
+         * <p>The target aspect ratio is used as a hint when determining the resulting output aspect
+         * ratio which may differ from the request, possibly due to device constraints.
+         * Application code should check the resulting output's resolution.
+         *
+         * <p>For Preview, the output is the SurfaceTexture of the
+         * {@link androidx.camera.core.Preview.PreviewOutput}.
+         *
+         * @param aspectRatio A {@link AspectRatio} representing the ratio of the
+         *                    target's width and height.
+         * @return The current Builder.
+         */
+        @NonNull
+        @Override
+        public Builder setTargetAspectRatio(@NonNull AspectRatio aspectRatio) {
             getMutableConfig().insertOption(OPTION_TARGET_ASPECT_RATIO, aspectRatio);
             return this;
         }
@@ -686,8 +777,8 @@
          * @param rotation The rotation of the intended target.
          * @return The current Builder.
          */
-        @Override
         @NonNull
+        @Override
         public Builder setTargetRotation(@RotationValue int rotation) {
             getMutableConfig().insertOption(OPTION_TARGET_ROTATION, rotation);
             return this;
@@ -703,14 +794,48 @@
          * target resolution, the nearest available resolution smaller than the target resolution
          * will be chosen.
          *
+         * <p>It is not allowed to set both target aspect ratio and target resolution on the same
+         * use case.
+         *
+         * <p>The target aspect ratio will also be set the same as the aspect ratio of the provided
+         * {@link Size}. Make sure to set the target resolution with the correct orientation.
+         *
          * @param resolution The target resolution to choose from supported output sizes list.
          * @return The current Builder.
          */
-        @Override
         @NonNull
+        @Override
         public Builder setTargetResolution(@NonNull Size resolution) {
             getMutableConfig()
                     .insertOption(ImageOutputConfig.OPTION_TARGET_RESOLUTION, resolution);
+            if (resolution != null) {
+                getMutableConfig().insertOption(OPTION_TARGET_ASPECT_RATIO_CUSTOM,
+                        new Rational(resolution.getWidth(), resolution.getHeight()));
+            }
+            return this;
+        }
+
+        /**
+         * Sets the default resolution of the intended target from this configuration.
+         *
+         * @param resolution The default resolution to choose from supported output sizes list.
+         * @return The current Builder.
+         * @hide
+         */
+        @NonNull
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        @Override
+        public Builder setDefaultResolution(@NonNull Size resolution) {
+            getMutableConfig().insertOption(OPTION_DEFAULT_RESOLUTION, resolution);
+            return this;
+        }
+
+        /** @hide */
+        @NonNull
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        @Override
+        public Builder setMaxResolution(@NonNull Size resolution) {
+            getMutableConfig().insertOption(OPTION_MAX_RESOLUTION, resolution);
             return this;
         }
 
@@ -718,27 +843,14 @@
         @RestrictTo(Scope.LIBRARY_GROUP)
         @Override
         @NonNull
-        public Builder setMaxResolution(@NonNull Size resolution) {
-            getMutableConfig().insertOption(OPTION_MAX_RESOLUTION, resolution);
+        public Builder setSupportedResolutions(@NonNull List<Pair<Integer, Size[]>> resolutions) {
+            getMutableConfig().insertOption(OPTION_SUPPORTED_RESOLUTIONS, resolutions);
             return this;
         }
 
         // Implementations of ThreadConfig.Builder default methods
 
         /**
-         * Sets the default handler that will be used for callbacks.
-         *
-         * @param handler The handler which will be used to post callbacks.
-         * @return the current Builder.
-         */
-        @Override
-        @NonNull
-        public Builder setCallbackHandler(@NonNull Handler handler) {
-            getMutableConfig().insertOption(OPTION_CALLBACK_HANDLER, handler);
-            return this;
-        }
-
-        /**
          * Sets the default executor that will be used for background tasks.
          *
          * @param executor The executor which will be used for background tasks.
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ProcessingSurfaceTexture.java b/camera/camera-core/src/main/java/androidx/camera/core/ProcessingSurfaceTexture.java
index 6744bd4..57b5262 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/ProcessingSurfaceTexture.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ProcessingSurfaceTexture.java
@@ -18,6 +18,7 @@
 
 import android.graphics.PixelFormat;
 import android.graphics.SurfaceTexture;
+import android.os.AsyncTask;
 import android.os.Handler;
 import android.os.Looper;
 import android.util.Log;
@@ -219,7 +220,7 @@
                             }
                         }
                     },
-                    mImageReaderHandler
+                    AsyncTask.THREAD_POOL_EXECUTOR
             );
 
             // Need to wait for Surface has been detached before closing it
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ThreadConfig.java b/camera/camera-core/src/main/java/androidx/camera/core/ThreadConfig.java
index c8814ec..679d446 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/ThreadConfig.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ThreadConfig.java
@@ -16,8 +16,6 @@
 
 package androidx.camera.core;
 
-import android.os.Handler;
-
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
@@ -38,16 +36,6 @@
     // *********************************************************************************************
 
     /**
-     * Option: camerax.core.thread.callbackHandler
-     *
-     * @hide
-     */
-    @RestrictTo(Scope.LIBRARY_GROUP)
-    Option<Handler> OPTION_CALLBACK_HANDLER =
-            Option.create("camerax.core.thread.callbackHandler", Handler.class);
-
-
-    /**
      * Option: camerax.core.thread.backgroundExecutor
      *
      * @hide
@@ -59,25 +47,6 @@
     // *********************************************************************************************
 
     /**
-     * Returns the default handler that will be used for callbacks.
-     *
-     * @param valueIfMissing The value to return if this configuration option has not been set.
-     * @return The stored value or <code>valueIfMissing</code> if the value does not exist in this
-     * configuration.
-     */
-    @Nullable
-    Handler getCallbackHandler(@Nullable Handler valueIfMissing);
-
-    /**
-     * Returns the default handler that will be used for callbacks.
-     *
-     * @return The stored value, if it exists in this configuration.
-     * @throws IllegalArgumentException if the option does not exist in this configuration.
-     */
-    @NonNull
-    Handler getCallbackHandler();
-
-    /**
      * Returns the executor that will be used for background tasks.
      *
      * @param valueIfMissing The value to return if this configuration option has not been set.
@@ -108,15 +77,6 @@
     interface Builder<B> {
 
         /**
-         * Sets the default handler that will be used for callbacks.
-         *
-         * @param handler The handler which will be used to post callbacks.
-         * @return the current Builder.
-         */
-        @NonNull
-        B setCallbackHandler(@NonNull Handler handler);
-
-        /**
          * Sets the default executor that will be used for background tasks.
          *
          * @param executor The executor which will be used for background tasks.
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/VideoCapture.java b/camera/camera-core/src/main/java/androidx/camera/core/VideoCapture.java
index 136e04f..e30326c 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/VideoCapture.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/VideoCapture.java
@@ -921,7 +921,6 @@
         static {
             VideoCaptureConfig.Builder builder =
                     new VideoCaptureConfig.Builder()
-                            .setCallbackHandler(DEFAULT_HANDLER)
                             .setVideoFrameRate(DEFAULT_VIDEO_FRAME_RATE)
                             .setBitRate(DEFAULT_BIT_RATE)
                             .setIFrameInterval(DEFAULT_INTRA_FRAME_INTERVAL)
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/VideoCaptureConfig.java b/camera/camera-core/src/main/java/androidx/camera/core/VideoCaptureConfig.java
index 03acc0b..9a35ee8 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/VideoCaptureConfig.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/VideoCaptureConfig.java
@@ -16,7 +16,7 @@
 
 package androidx.camera.core;
 
-import android.os.Handler;
+import android.util.Pair;
 import android.util.Rational;
 import android.util.Size;
 import android.view.Surface;
@@ -26,6 +26,7 @@
 import androidx.annotation.RestrictTo;
 import androidx.annotation.RestrictTo.Scope;
 
+import java.util.List;
 import java.util.Set;
 import java.util.UUID;
 import java.util.concurrent.Executor;
@@ -428,11 +429,13 @@
      * @param valueIfMissing The value to return if this configuration option has not been set.
      * @return The stored value or <code>valueIfMissing</code> if the value does not exist in this
      * configuration.
+     * @hide
      */
+    @RestrictTo(Scope.LIBRARY_GROUP)
     @Override
     @Nullable
-    public Rational getTargetAspectRatio(@Nullable Rational valueIfMissing) {
-        return retrieveOption(OPTION_TARGET_ASPECT_RATIO, valueIfMissing);
+    public Rational getTargetAspectRatioCustom(@Nullable Rational valueIfMissing) {
+        return retrieveOption(OPTION_TARGET_ASPECT_RATIO_CUSTOM, valueIfMissing);
     }
 
     /**
@@ -444,10 +447,37 @@
      *
      * @return The stored value, if it exists in this configuration.
      * @throws IllegalArgumentException if the option does not exist in this configuration.
+     * @hide
      */
-    @Override
     @NonNull
-    public Rational getTargetAspectRatio() {
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Override
+    public Rational getTargetAspectRatioCustom() {
+        return retrieveOption(OPTION_TARGET_ASPECT_RATIO_CUSTOM);
+    }
+
+    /**
+     * Retrieves the aspect ratio of the target intending to use images from this configuration.
+     *
+     * @param valueIfMissing The value to return if this configuration option has not been set.
+     * @return The stored value or <code>valueIfMissing</code> if the value does not exist in this
+     * configuration.
+     */
+    @Nullable
+    @Override
+    public AspectRatio getTargetAspectRatio(@Nullable AspectRatio valueIfMissing) {
+        return retrieveOption(OPTION_TARGET_ASPECT_RATIO, valueIfMissing);
+    }
+
+    /**
+     * Retrieves the aspect ratio of the target intending to use images from this configuration.
+     *
+     * @return The stored value, if it exists in this configuration.
+     * @throws IllegalArgumentException if the option does not exist in this configuration.
+     */
+    @NonNull
+    @Override
+    public AspectRatio getTargetAspectRatio() {
         return retrieveOption(OPTION_TARGET_ASPECT_RATIO);
     }
 
@@ -500,6 +530,35 @@
         return retrieveOption(ImageOutputConfig.OPTION_TARGET_RESOLUTION);
     }
 
+    /**
+     * Retrieves the default resolution of the target intending to use from this configuration.
+     *
+     * @param valueIfMissing The value to return if this configuration option has not been set.
+     * @return The stored value or <code>valueIfMissing</code> if the value does not exist in this
+     * configuration.
+     * @hide
+     */
+    @Nullable
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Override
+    public Size getDefaultResolution(@Nullable Size valueIfMissing) {
+        return retrieveOption(ImageOutputConfig.OPTION_DEFAULT_RESOLUTION, valueIfMissing);
+    }
+
+    /**
+     * Retrieves the default resolution of the target intending to use from this configuration.
+     *
+     * @return The stored value, if it exists in this configuration.
+     * @throws IllegalArgumentException if the option does not exist in this configuration.
+     * @hide
+     */
+    @NonNull
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Override
+    public Size getDefaultResolution() {
+        return retrieveOption(ImageOutputConfig.OPTION_DEFAULT_RESOLUTION);
+    }
+
     /** @hide */
     @RestrictTo(Scope.LIBRARY_GROUP)
     @Override
@@ -516,33 +575,25 @@
         return retrieveOption(OPTION_MAX_RESOLUTION);
     }
 
-    // Implementations of ThreadConfig default methods
-
-    /**
-     * Returns the default handler that will be used for callbacks.
-     *
-     * @param valueIfMissing The value to return if this configuration option has not been set.
-     * @return The stored value or <code>valueIfMissing</code> if the value does not exist in this
-     * configuration.
-     */
+    /** @hide */
+    @RestrictTo(Scope.LIBRARY_GROUP)
     @Override
     @Nullable
-    public Handler getCallbackHandler(@Nullable Handler valueIfMissing) {
-        return retrieveOption(OPTION_CALLBACK_HANDLER, valueIfMissing);
+    public List<Pair<Integer, Size[]>> getSupportedResolutions(
+            @Nullable List<Pair<Integer, Size[]>> valueIfMissing) {
+        return retrieveOption(OPTION_SUPPORTED_RESOLUTIONS, valueIfMissing);
     }
 
-    /**
-     * Returns the default handler that will be used for callbacks.
-     *
-     * @return The stored value, if it exists in this configuration.
-     * @throws IllegalArgumentException if the option does not exist in this configuration.
-     */
+    /** @hide */
+    @RestrictTo(Scope.LIBRARY_GROUP)
     @Override
     @NonNull
-    public Handler getCallbackHandler() {
-        return retrieveOption(OPTION_CALLBACK_HANDLER);
+    public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+        return retrieveOption(OPTION_SUPPORTED_RESOLUTIONS);
     }
 
+    // Implementations of ThreadConfig default methods
+
     /**
      * Returns the executor that will be used for background tasks.
      *
@@ -729,6 +780,14 @@
          */
         @NonNull
         public VideoCaptureConfig build() {
+            // Error at runtime for using both setTargetResolution and setTargetAspectRatio on
+            // the same config.
+            if (getMutableConfig().retrieveOption(OPTION_TARGET_ASPECT_RATIO, null) != null
+                    && getMutableConfig().retrieveOption(OPTION_TARGET_RESOLUTION, null) != null) {
+                throw new IllegalArgumentException(
+                        "Cannot use both setTargetResolution and setTargetAspectRatio on the same "
+                                + "config.");
+            }
             return new VideoCaptureConfig(OptionsBundle.from(mMutableConfig));
         }
 
@@ -922,15 +981,44 @@
          * ratio which may differ from the request, possibly due to device constraints.
          * Application code should check the resulting output's resolution.
          *
+         * <p>This method can be used to request an aspect ratio that is not from the standard set
+         * of aspect ratios defined in the {@link AspectRatio}.
+         *
+         * <p>This method will remove any value set by setTargetAspectRatio().
+         *
          * <p>For VideoCapture the output is the output video file.
          *
          * @param aspectRatio A {@link Rational} representing the ratio of the target's width and
          *                    height.
          * @return The current Builder.
+         * @hide
          */
-        @Override
         @NonNull
-        public Builder setTargetAspectRatio(@NonNull Rational aspectRatio) {
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        @Override
+        public Builder setTargetAspectRatioCustom(@NonNull Rational aspectRatio) {
+            getMutableConfig().insertOption(OPTION_TARGET_ASPECT_RATIO_CUSTOM, aspectRatio);
+            getMutableConfig().removeOption(OPTION_TARGET_ASPECT_RATIO);
+            return this;
+        }
+
+        /**
+         * Sets the aspect ratio of the intended target for images from this configuration.
+         *
+         * <p>It is not allowed to set both target aspect ratio and target resolution on the same
+         * use case.
+         *
+         * <p>The target aspect ratio is used as a hint when determining the resulting output aspect
+         * ratio which may differ from the request, possibly due to device constraints.
+         * Application code should check the resulting output's resolution.
+         *
+         * @param aspectRatio A {@link AspectRatio} representing the ratio of the
+         *                    target's width and height.
+         * @return The current Builder.
+         */
+        @NonNull
+        @Override
+        public Builder setTargetAspectRatio(@NonNull AspectRatio aspectRatio) {
             getMutableConfig().insertOption(OPTION_TARGET_ASPECT_RATIO, aspectRatio);
             return this;
         }
@@ -945,46 +1033,79 @@
          * @param rotation The rotation of the intended target.
          * @return The current Builder.
          */
-        @Override
         @NonNull
+        @Override
         public Builder setTargetRotation(@RotationValue int rotation) {
             getMutableConfig().insertOption(OPTION_TARGET_ROTATION, rotation);
             return this;
         }
 
-        /** @hide */
+        /**
+         * Sets the resolution of the intended target from this configuration.
+         *
+         * <p>The target resolution attempts to establish a minimum bound for the image resolution.
+         * The actual image resolution will be the closest available resolution in size that is not
+         * smaller than the target resolution, as determined by the Camera implementation. However,
+         * if no resolution exists that is equal to or larger than the target resolution, the
+         * nearest available resolution smaller than the target resolution will be chosen.
+         *
+         * <p>It is not allowed to set both target aspect ratio and target resolution on the same
+         * use case.
+         *
+         * <p>The target aspect ratio will also be set the same as the aspect ratio of the provided
+         * {@link Size}. Make sure to set the target resolution with the correct orientation.
+         *
+         * @param resolution The target resolution to choose from supported output sizes list.
+         * @return The current Builder.
+         * @hide
+         */
+        @NonNull
         @RestrictTo(Scope.LIBRARY_GROUP)
         @Override
-        @NonNull
         public Builder setTargetResolution(@NonNull Size resolution) {
             getMutableConfig().insertOption(OPTION_TARGET_RESOLUTION, resolution);
+            if (resolution != null) {
+                getMutableConfig().insertOption(OPTION_TARGET_ASPECT_RATIO_CUSTOM,
+                        new Rational(resolution.getWidth(), resolution.getHeight()));
+            }
             return this;
         }
 
-        /** @hide */
+        /**
+         * Sets the default resolution of the intended target from this configuration.
+         *
+         * @param resolution The default resolution to choose from supported output sizes list.
+         * @return The current Builder.
+         * @hide
+         */
+        @NonNull
         @RestrictTo(Scope.LIBRARY_GROUP)
         @Override
+        public Builder setDefaultResolution(@NonNull Size resolution) {
+            getMutableConfig().insertOption(OPTION_DEFAULT_RESOLUTION, resolution);
+            return null;
+        }
+
+        /** @hide */
         @NonNull
+        @RestrictTo(Scope.LIBRARY_GROUP)
+        @Override
         public Builder setMaxResolution(@NonNull Size resolution) {
             getMutableConfig().insertOption(OPTION_MAX_RESOLUTION, resolution);
             return this;
         }
 
-        // Implementations of ThreadConfig.Builder default methods
-
-        /**
-         * Sets the default handler that will be used for callbacks.
-         *
-         * @param handler The handler which will be used to post callbacks.
-         * @return the current Builder.
-         */
+        /** @hide */
+        @RestrictTo(Scope.LIBRARY_GROUP)
         @Override
         @NonNull
-        public Builder setCallbackHandler(@NonNull Handler handler) {
-            getMutableConfig().insertOption(OPTION_CALLBACK_HANDLER, handler);
+        public Builder setSupportedResolutions(@NonNull List<Pair<Integer, Size[]>> resolutions) {
+            getMutableConfig().insertOption(OPTION_SUPPORTED_RESOLUTIONS, resolutions);
             return this;
         }
 
+        // Implementations of ThreadConfig.Builder default methods
+
         /**
          * Sets the default executor that will be used for background tasks.
          *
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/ImageAnalysisNonBlockingAnalyzerTest.java b/camera/camera-core/src/test/java/androidx/camera/core/ImageAnalysisNonBlockingAnalyzerTest.java
index b2d6555..1acb499 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/ImageAnalysisNonBlockingAnalyzerTest.java
+++ b/camera/camera-core/src/test/java/androidx/camera/core/ImageAnalysisNonBlockingAnalyzerTest.java
@@ -23,8 +23,6 @@
 import static org.mockito.Mockito.when;
 
 import android.os.Build;
-import android.os.Handler;
-import android.os.Looper;
 
 import androidx.camera.core.impl.utils.executor.CameraXExecutors;
 import androidx.test.filters.SmallTest;
@@ -62,7 +60,7 @@
         mImageAnalysisNonBlockingAnalyzer = new ImageAnalysisNonBlockingAnalyzer(
                 new AtomicReference<ImageAnalysis.Analyzer>(mAnalyzer),
                 ROTATION,
-                new Handler(Looper.getMainLooper()),
+                new AtomicReference<>(CameraXExecutors.mainThreadExecutor()),
                 CameraXExecutors.directExecutor()
         );
     }
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/ImageAnalysisTest.java b/camera/camera-core/src/test/java/androidx/camera/core/ImageAnalysisTest.java
index cec54b0..0c29520 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/ImageAnalysisTest.java
+++ b/camera/camera-core/src/test/java/androidx/camera/core/ImageAnalysisTest.java
@@ -197,18 +197,18 @@
 
     private void setUpImageAnalysisWithMode(ImageAnalysis.ImageReaderMode imageReaderMode) {
         mImageAnalysis = new ImageAnalysis(new ImageAnalysisConfig.Builder()
-                .setCallbackHandler(mCallbackHandler)
                 .setBackgroundExecutor(mBackgroundExecutor)
                 .setImageQueueDepth(QUEUE_DEPTH)
                 .setImageReaderMode(imageReaderMode)
                 .build());
 
-        mImageAnalysis.setAnalyzer(new ImageAnalysis.Analyzer() {
-            @Override
-            public void analyze(ImageProxy image, int rotationDegrees) {
-                mImagesReceived.add(image.getImage());
-            }
-        });
+        mImageAnalysis.setAnalyzer(CameraXExecutors.newHandlerExecutor(mCallbackHandler),
+                new ImageAnalysis.Analyzer() {
+                    @Override
+                    public void analyze(ImageProxy image, int rotationDegrees) {
+                        mImagesReceived.add(image.getImage());
+                    }
+                });
 
         Map<String, Size> suggestedResolutionMap = new HashMap<>();
         suggestedResolutionMap.put(ShadowCameraX.DEFAULT_CAMERA_ID, DEFAULT_RESOLUTION);
diff --git a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/AutoImageCaptureExtenderImpl.java b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/AutoImageCaptureExtenderImpl.java
index 2bdb75a..1a85283 100755
--- a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/AutoImageCaptureExtenderImpl.java
+++ b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/AutoImageCaptureExtenderImpl.java
@@ -17,6 +17,8 @@
 
 import android.content.Context;
 import android.hardware.camera2.CameraCharacteristics;
+import android.util.Pair;
+import android.util.Size;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -82,4 +84,9 @@
     public CaptureStageImpl onDisableSession() {
         throw new RuntimeException("Stub, replace with implementation.");
     }
+
+    @Override
+    public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
 }
diff --git a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/AutoPreviewExtenderImpl.java b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/AutoPreviewExtenderImpl.java
index cddbb31..222c617 100755
--- a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/AutoPreviewExtenderImpl.java
+++ b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/AutoPreviewExtenderImpl.java
@@ -18,10 +18,14 @@
 
 import android.content.Context;
 import android.hardware.camera2.CameraCharacteristics;
+import android.util.Pair;
+import android.util.Size;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
+import java.util.List;
+
 /**
  * Stub implementation for auto preview use case.
  *
@@ -82,4 +86,9 @@
     public CaptureStageImpl onDisableSession() {
         throw new RuntimeException("Stub, replace with implementation.");
     }
+
+    @Override
+    public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
 }
diff --git a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/BeautyImageCaptureExtenderImpl.java b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/BeautyImageCaptureExtenderImpl.java
index b08a2a3..e1cac6c 100755
--- a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/BeautyImageCaptureExtenderImpl.java
+++ b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/BeautyImageCaptureExtenderImpl.java
@@ -17,6 +17,8 @@
 
 import android.content.Context;
 import android.hardware.camera2.CameraCharacteristics;
+import android.util.Pair;
+import android.util.Size;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -82,4 +84,9 @@
     public CaptureStageImpl onDisableSession() {
         throw new RuntimeException("Stub, replace with implementation.");
     }
+
+    @Override
+    public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
 }
diff --git a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/BeautyPreviewExtenderImpl.java b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/BeautyPreviewExtenderImpl.java
index cc04bec..e8afee2 100755
--- a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/BeautyPreviewExtenderImpl.java
+++ b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/BeautyPreviewExtenderImpl.java
@@ -18,10 +18,14 @@
 
 import android.content.Context;
 import android.hardware.camera2.CameraCharacteristics;
+import android.util.Pair;
+import android.util.Size;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
+import java.util.List;
+
 /**
  * Stub implementation for beauty preview use case.
  *
@@ -82,4 +86,9 @@
     public CaptureStageImpl onDisableSession() {
         throw new RuntimeException("Stub, replace with implementation.");
     }
+
+    @Override
+    public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
 }
diff --git a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/BokehImageCaptureExtenderImpl.java b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/BokehImageCaptureExtenderImpl.java
index 49cb42c..a76253f 100644
--- a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/BokehImageCaptureExtenderImpl.java
+++ b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/BokehImageCaptureExtenderImpl.java
@@ -17,6 +17,8 @@
 
 import android.content.Context;
 import android.hardware.camera2.CameraCharacteristics;
+import android.util.Pair;
+import android.util.Size;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -82,4 +84,9 @@
     public CaptureStageImpl onDisableSession() {
         throw new RuntimeException("Stub, replace with implementation.");
     }
+
+    @Override
+    public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
 }
diff --git a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/BokehPreviewExtenderImpl.java b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/BokehPreviewExtenderImpl.java
index 85b6f00..172ff90 100644
--- a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/BokehPreviewExtenderImpl.java
+++ b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/BokehPreviewExtenderImpl.java
@@ -17,10 +17,14 @@
 
 import android.content.Context;
 import android.hardware.camera2.CameraCharacteristics;
+import android.util.Pair;
+import android.util.Size;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
+import java.util.List;
+
 /**
  * Stub implementation for bokeh preview use case.
  *
@@ -80,4 +84,9 @@
     public CaptureStageImpl onDisableSession() {
         throw new RuntimeException("Stub, replace with implementation.");
     }
+
+    @Override
+    public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
 }
diff --git a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/HdrImageCaptureExtenderImpl.java b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/HdrImageCaptureExtenderImpl.java
index 0d7434d..907cb72 100644
--- a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/HdrImageCaptureExtenderImpl.java
+++ b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/HdrImageCaptureExtenderImpl.java
@@ -17,6 +17,8 @@
 
 import android.content.Context;
 import android.hardware.camera2.CameraCharacteristics;
+import android.util.Pair;
+import android.util.Size;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -82,4 +84,9 @@
     public CaptureStageImpl onDisableSession() {
         throw new RuntimeException("Stub, replace with implementation.");
     }
+
+    @Override
+    public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
 }
diff --git a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/HdrPreviewExtenderImpl.java b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/HdrPreviewExtenderImpl.java
index 536d51c..f5c2532 100644
--- a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/HdrPreviewExtenderImpl.java
+++ b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/HdrPreviewExtenderImpl.java
@@ -18,10 +18,14 @@
 
 import android.content.Context;
 import android.hardware.camera2.CameraCharacteristics;
+import android.util.Pair;
+import android.util.Size;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
+import java.util.List;
+
 /**
  * Stub implementation for HDR preview use case.
  *
@@ -82,4 +86,9 @@
     public CaptureStageImpl onDisableSession() {
         throw new RuntimeException("Stub, replace with implementation.");
     }
+
+    @Override
+    public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
 }
diff --git a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/ImageCaptureExtenderImpl.java b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/ImageCaptureExtenderImpl.java
index 6c5c124..702e6c0 100644
--- a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/ImageCaptureExtenderImpl.java
+++ b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/ImageCaptureExtenderImpl.java
@@ -16,7 +16,12 @@
 
 package androidx.camera.extensions.impl;
 
+import android.graphics.ImageFormat;
 import android.hardware.camera2.CameraCharacteristics;
+import android.util.Pair;
+import android.util.Size;
+
+import androidx.annotation.Nullable;
 
 import java.util.List;
 
@@ -57,4 +62,20 @@
      * @return the maximum count.
      */
     int getMaxCaptureStage();
+
+    /**
+     * Returns the customized supported resolutions.
+     *
+     * <p>Pair list composed with {@link ImageFormat} and {@link Size} array will be returned.
+     *
+     * <p>The returned resolutions should be subset of the supported sizes retrieved from
+     * {@link android.hardware.camera2.params.StreamConfigurationMap} for the camera device. If the
+     * returned list is not null, it will be used to find the best resolutions combination for
+     * the bound use cases.
+     *
+     * @return the customized supported resolutions.
+     * @since 1.1
+     */
+    @Nullable
+    List<Pair<Integer, Size[]>> getSupportedResolutions();
 }
diff --git a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/NightImageCaptureExtenderImpl.java b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/NightImageCaptureExtenderImpl.java
index f857a48..861a4fd 100755
--- a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/NightImageCaptureExtenderImpl.java
+++ b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/NightImageCaptureExtenderImpl.java
@@ -17,6 +17,8 @@
 
 import android.content.Context;
 import android.hardware.camera2.CameraCharacteristics;
+import android.util.Pair;
+import android.util.Size;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -82,4 +84,9 @@
     public CaptureStageImpl onDisableSession() {
         throw new RuntimeException("Stub, replace with implementation.");
     }
+
+    @Override
+    public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
 }
diff --git a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/NightPreviewExtenderImpl.java b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/NightPreviewExtenderImpl.java
index 5281f65..4d34852 100755
--- a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/NightPreviewExtenderImpl.java
+++ b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/NightPreviewExtenderImpl.java
@@ -18,10 +18,14 @@
 
 import android.content.Context;
 import android.hardware.camera2.CameraCharacteristics;
+import android.util.Pair;
+import android.util.Size;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
+import java.util.List;
+
 /**
  * Stub implementation for night preview use case.
  *
@@ -82,4 +86,9 @@
     public CaptureStageImpl onDisableSession() {
         throw new RuntimeException("Stub, replace with implementation.");
     }
+
+    @Override
+    public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+        throw new RuntimeException("Stub, replace with implementation.");
+    }
 }
diff --git a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/PreviewExtenderImpl.java b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/PreviewExtenderImpl.java
index cfe2995..d690da3 100644
--- a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/PreviewExtenderImpl.java
+++ b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/PreviewExtenderImpl.java
@@ -16,8 +16,15 @@
 
 package androidx.camera.extensions.impl;
 
+import android.graphics.ImageFormat;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.TotalCaptureResult;
+import android.util.Pair;
+import android.util.Size;
+
+import androidx.annotation.Nullable;
+
+import java.util.List;
 
 /**
  * Provides abstract methods that the OEM needs to implement to enable extensions in the preview.
@@ -76,4 +83,20 @@
      *
      */
     ProcessorImpl getProcessor();
+
+    /**
+     * Returns the customized supported resolutions.
+     *
+     * <p>Pair list composed with {@link ImageFormat} and {@link Size} array will be returned.
+     *
+     * <p>The returned resolutions should be subset of the supported sizes retrieved from
+     * {@link android.hardware.camera2.params.StreamConfigurationMap} for the camera device. If the
+     * returned list is not null, it will be used to find the best resolutions combination for
+     * the bound use cases.
+     *
+     * @return the customized supported resolutions.
+     * @since 1.1
+     */
+    @Nullable
+    List<Pair<Integer, Size[]>> getSupportedResolutions();
 }
diff --git a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ImageCaptureExtenderTest.java b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ImageCaptureExtenderTest.java
index 38ea33d..9977b2c 100644
--- a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ImageCaptureExtenderTest.java
+++ b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ImageCaptureExtenderTest.java
@@ -16,6 +16,8 @@
 
 package androidx.camera.extensions;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.junit.Assume.assumeTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.atLeastOnce;
@@ -29,17 +31,22 @@
 import android.Manifest;
 import android.app.Instrumentation;
 import android.content.Context;
+import android.hardware.camera2.CameraAccessException;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CaptureRequest;
 import android.util.Pair;
+import android.util.Size;
 
 import androidx.camera.camera2.Camera2AppConfig;
 import androidx.camera.camera2.Camera2Config;
 import androidx.camera.camera2.impl.CameraEventCallbacks;
+import androidx.camera.core.CameraDeviceConfig;
+import androidx.camera.core.CameraInfoUnavailableException;
 import androidx.camera.core.CameraX;
 import androidx.camera.core.CaptureProcessor;
 import androidx.camera.core.ImageCapture;
 import androidx.camera.core.ImageCaptureConfig;
+import androidx.camera.extensions.impl.BeautyImageCaptureExtenderImpl;
 import androidx.camera.extensions.impl.CaptureStageImpl;
 import androidx.camera.extensions.impl.ImageCaptureExtenderImpl;
 import androidx.camera.testing.CameraUtil;
@@ -47,6 +54,7 @@
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.MediumTest;
+import androidx.test.filters.SmallTest;
 import androidx.test.platform.app.InstrumentationRegistry;
 import androidx.test.rule.GrantPermissionRule;
 
@@ -58,6 +66,7 @@
 import org.mockito.InOrder;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 import java.util.concurrent.ExecutionException;
 
@@ -194,6 +203,58 @@
         verifyNoMoreInteractions(mMockImageCaptureExtenderImpl);
     }
 
+    @Test
+    @SmallTest
+    public void canSetSupportedResolutionsToConfigTest()
+            throws CameraInfoUnavailableException, CameraAccessException {
+        CameraX.LensFacing lensFacing = CameraX.LensFacing.BACK;
+        assumeTrue(CameraUtil.hasCameraWithLensFacing(lensFacing));
+        ImageCaptureConfig.Builder configBuilder = new ImageCaptureConfig.Builder().setLensFacing(
+                lensFacing);
+
+        String cameraId = androidx.camera.extensions.CameraUtil.getCameraId(
+                ((CameraDeviceConfig) configBuilder.build()));
+        CameraCharacteristics cameraCharacteristics =
+                CameraUtil.getCameraManager().getCameraCharacteristics(
+                        CameraX.getCameraWithLensFacing(lensFacing));
+
+        // Only BeautyImageCaptureExtenderImpl has sample implementation for ImageCapture.
+        // Retrieves the target format/resolutions pair list directly from
+        // BeautyImageCaptureExtenderImpl.
+        BeautyImageCaptureExtenderImpl impl = new BeautyImageCaptureExtenderImpl();
+
+        impl.init(cameraId, cameraCharacteristics);
+        List<Pair<Integer, Size[]>> targetFormatResolutionsPairList =
+                impl.getSupportedResolutions();
+
+        assertThat(targetFormatResolutionsPairList).isNotNull();
+
+        // Retrieves the target format/resolutions pair list from builder after applying beauty
+        // mode.
+        BeautyImageCaptureExtender extender = BeautyImageCaptureExtender.create(configBuilder);
+        assertThat(configBuilder.build().getSupportedResolutions(null)).isNull();
+        extender.enableExtension();
+
+        List<Pair<Integer, Size[]>> resultFormatResolutionsPairList =
+                configBuilder.build().getSupportedResolutions(null);
+
+        assertThat(resultFormatResolutionsPairList).isNotNull();
+
+        // Checks the result and target pair lists are the same
+        for (Pair<Integer, Size[]> resultPair : resultFormatResolutionsPairList) {
+            Size[] targetSizes = null;
+            for (Pair<Integer, Size[]> targetPair : targetFormatResolutionsPairList) {
+                if (targetPair.first.equals(resultPair.first)) {
+                    targetSizes = targetPair.second;
+                    break;
+                }
+            }
+
+            assertThat(Arrays.asList(resultPair.second).equals(
+                    Arrays.asList(targetSizes))).isTrue();
+        }
+    }
+
     final class FakeCaptureStage implements CaptureStageImpl {
 
         @Override
diff --git a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/PreviewExtenderTest.java b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/PreviewExtenderTest.java
index 8915480..caa620e 100644
--- a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/PreviewExtenderTest.java
+++ b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/PreviewExtenderTest.java
@@ -31,16 +31,21 @@
 import android.Manifest;
 import android.app.Instrumentation;
 import android.content.Context;
+import android.hardware.camera2.CameraAccessException;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.TotalCaptureResult;
 import android.media.Image;
 import android.util.Pair;
+import android.util.Size;
 
 import androidx.camera.camera2.Camera2AppConfig;
+import androidx.camera.core.CameraDeviceConfig;
+import androidx.camera.core.CameraInfoUnavailableException;
 import androidx.camera.core.CameraX;
 import androidx.camera.core.Preview;
 import androidx.camera.core.PreviewConfig;
+import androidx.camera.extensions.impl.BeautyPreviewExtenderImpl;
 import androidx.camera.extensions.impl.CaptureStageImpl;
 import androidx.camera.extensions.impl.PreviewExtenderImpl;
 import androidx.camera.extensions.impl.PreviewImageProcessorImpl;
@@ -50,6 +55,7 @@
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.MediumTest;
+import androidx.test.filters.SmallTest;
 import androidx.test.platform.app.InstrumentationRegistry;
 import androidx.test.rule.GrantPermissionRule;
 
@@ -62,6 +68,7 @@
 import org.mockito.InOrder;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 import java.util.concurrent.ExecutionException;
 
@@ -139,6 +146,7 @@
                 any(CameraCharacteristics.class));
         verify(mockPreviewExtenderImpl, timeout(3000)).getProcessorType();
         verify(mockPreviewExtenderImpl, timeout(3000)).getProcessor();
+        verify(mockPreviewExtenderImpl, timeout(3000)).getSupportedResolutions();
 
         InOrder inOrder = inOrder(ignoreStubs(mockPreviewExtenderImpl));
 
@@ -258,6 +266,57 @@
                 any(TotalCaptureResult.class));
     }
 
+    @Test
+    @SmallTest
+    public void canSetSupportedResolutionsToConfigTest()
+            throws CameraInfoUnavailableException, CameraAccessException {
+        CameraX.LensFacing lensFacing = CameraX.LensFacing.BACK;
+        assumeTrue(CameraUtil.hasCameraWithLensFacing(lensFacing));
+        PreviewConfig.Builder configBuilder = new PreviewConfig.Builder().setLensFacing(lensFacing);
+
+        String cameraId = androidx.camera.extensions.CameraUtil.getCameraId(
+                ((CameraDeviceConfig) configBuilder.build()));
+        CameraCharacteristics cameraCharacteristics =
+                CameraUtil.getCameraManager().getCameraCharacteristics(
+                        CameraX.getCameraWithLensFacing(lensFacing));
+
+        // Only BeautyPreviewExtenderImpl has sample implementation for Preview.
+        // Retrieves the target format/resolutions pair list directly from
+        // BeautyPreviewExtenderImpl.
+        BeautyPreviewExtenderImpl impl = new BeautyPreviewExtenderImpl();
+
+        impl.init(cameraId, cameraCharacteristics);
+        List<Pair<Integer, Size[]>> targetFormatResolutionsPairList =
+                impl.getSupportedResolutions();
+
+        assertThat(targetFormatResolutionsPairList).isNotNull();
+
+        // Retrieves the target format/resolutions pair list from builder after applying beauty
+        // mode.
+        BeautyPreviewExtender extender = BeautyPreviewExtender.create(configBuilder);
+        assertThat(configBuilder.build().getSupportedResolutions(null)).isNull();
+        extender.enableExtension();
+
+        List<Pair<Integer, Size[]>> resultFormatResolutionsPairList =
+                configBuilder.build().getSupportedResolutions(null);
+
+        assertThat(resultFormatResolutionsPairList).isNotNull();
+
+        // Checks the result and target pair lists are the same
+        for (Pair<Integer, Size[]> resultPair : resultFormatResolutionsPairList) {
+            Size[] targetSizes = null;
+            for (Pair<Integer, Size[]> targetPair : targetFormatResolutionsPairList) {
+                if (targetPair.first.equals(resultPair.first)) {
+                    targetSizes = targetPair.second;
+                    break;
+                }
+            }
+
+            assertThat(Arrays.asList(resultPair.second).equals(
+                    Arrays.asList(targetSizes))).isTrue();
+        }
+    }
+
     private class FakePreviewExtender extends PreviewExtender {
         FakePreviewExtender(PreviewConfig.Builder builder, PreviewExtenderImpl impl) {
             init(builder, impl, ExtensionsManager.EffectMode.NORMAL);
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/ImageCaptureExtender.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/ImageCaptureExtender.java
index 0880084..7cc2b91 100644
--- a/camera/camera-extensions/src/main/java/androidx/camera/extensions/ImageCaptureExtender.java
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/ImageCaptureExtender.java
@@ -19,6 +19,9 @@
 import android.hardware.camera2.CameraCharacteristics;
 import android.os.Handler;
 import android.os.Looper;
+import android.util.Log;
+import android.util.Pair;
+import android.util.Size;
 
 import androidx.annotation.GuardedBy;
 import androidx.annotation.NonNull;
@@ -51,6 +54,7 @@
  * Class for using an OEM provided extension on image capture.
  */
 public abstract class ImageCaptureExtender {
+    private static final String TAG = "ImageCaptureExtender";
     static final Config.Option<EffectMode> OPTION_IMAGE_CAPTURE_EXTENDER_MODE =
             Config.Option.create("camerax.extensions.imageCaptureExtender.mode", EffectMode.class);
 
@@ -130,6 +134,21 @@
         mBuilder.setUseCaseEventListener(imageCaptureAdapter);
         mBuilder.setCaptureBundle(imageCaptureAdapter);
         mBuilder.getMutableConfig().insertOption(OPTION_IMAGE_CAPTURE_EXTENDER_MODE, mEffectMode);
+        setSupportedResolutions();
+    }
+
+    private void setSupportedResolutions() {
+        List<Pair<Integer, Size[]>> supportedResolutions = null;
+
+        try {
+            supportedResolutions = mImpl.getSupportedResolutions();
+        } catch (NoSuchMethodError e) {
+            Log.e(TAG, "getSupportedResolution interface is not implemented in vendor library.");
+        }
+
+        if (supportedResolutions != null) {
+            mBuilder.setSupportedResolutions(supportedResolutions);
+        }
     }
 
     static void checkPreviewEnabled(EffectMode effectMode, Collection<UseCase> activeUseCases) {
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/PreviewExtender.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/PreviewExtender.java
index 3590207..4c1eab1 100644
--- a/camera/camera-extensions/src/main/java/androidx/camera/extensions/PreviewExtender.java
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/PreviewExtender.java
@@ -19,6 +19,9 @@
 import android.hardware.camera2.CameraCharacteristics;
 import android.os.Handler;
 import android.os.Looper;
+import android.util.Log;
+import android.util.Pair;
+import android.util.Size;
 
 import androidx.annotation.GuardedBy;
 import androidx.annotation.NonNull;
@@ -40,12 +43,14 @@
 import androidx.camera.extensions.impl.PreviewImageProcessorImpl;
 
 import java.util.Collection;
+import java.util.List;
 import java.util.Set;
 
 /**
  * Class for using an OEM provided extension on preview.
  */
 public abstract class PreviewExtender {
+    private static final String TAG = "PreviewExtender";
     static final Config.Option<EffectMode> OPTION_PREVIEW_EXTENDER_MODE = Config.Option.create(
             "camerax.extensions.previewExtender.mode", EffectMode.class);
 
@@ -135,6 +140,21 @@
                 new CameraEventCallbacks(previewExtenderAdapter));
         mBuilder.setUseCaseEventListener(previewExtenderAdapter);
         mBuilder.getMutableConfig().insertOption(OPTION_PREVIEW_EXTENDER_MODE, mEffectMode);
+        setSupportedResolutions();
+    }
+
+    private void setSupportedResolutions() {
+        List<Pair<Integer, Size[]>> supportedResolutions = null;
+
+        try {
+            supportedResolutions = mImpl.getSupportedResolutions();
+        } catch (NoSuchMethodError e) {
+            Log.e(TAG, "getSupportedResolution interface is not implemented in vendor library.");
+        }
+
+        if (supportedResolutions != null) {
+            mBuilder.setSupportedResolutions(supportedResolutions);
+        }
     }
 
     static void checkImageCaptureEnabled(EffectMode effectMode,
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraDeviceSurfaceManager.java b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraDeviceSurfaceManager.java
index 814b924..0216939 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraDeviceSurfaceManager.java
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraDeviceSurfaceManager.java
@@ -104,7 +104,7 @@
     @Override
     public Rational getCorrectedAspectRatio(@NonNull UseCaseConfig<?> useCaseConfig) {
         ImageOutputConfig config = (ImageOutputConfig) useCaseConfig;
-        Rational aspectRatio = config.getTargetAspectRatio(null);
+        Rational aspectRatio = config.getTargetAspectRatioCustom(null);
         return aspectRatio;
     }
 }
diff --git a/camera/camera-view/src/main/java/androidx/camera/view/CameraXModule.java b/camera/camera-view/src/main/java/androidx/camera/view/CameraXModule.java
index 27f6356..fac0352 100644
--- a/camera/camera-view/src/main/java/androidx/camera/view/CameraXModule.java
+++ b/camera/camera-view/src/main/java/androidx/camera/view/CameraXModule.java
@@ -34,6 +34,7 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresPermission;
 import androidx.annotation.UiThread;
+import androidx.camera.core.AspectRatio;
 import androidx.camera.core.CameraInfo;
 import androidx.camera.core.CameraInfoUnavailableException;
 import androidx.camera.core.CameraOrientationUtil;
@@ -216,22 +217,18 @@
 
         // Set the preferred aspect ratio as 4:3 if it is IMAGE only mode. Set the preferred aspect
         // ratio as 16:9 if it is VIDEO or MIXED mode. Then, it will be WYSIWYG when the view finder
-        // is
-        // in CENTER_INSIDE mode.
+        // is in CENTER_INSIDE mode.
 
         boolean isDisplayPortrait = getDisplayRotationDegrees() == 0
                 || getDisplayRotationDegrees() == 180;
 
+        Rational targetAspectRatio;
         if (getCaptureMode() == CaptureMode.IMAGE) {
-            mImageCaptureConfigBuilder.setTargetAspectRatio(
-                    isDisplayPortrait ? ASPECT_RATIO_3_4 : ASPECT_RATIO_4_3);
-            mPreviewConfigBuilder.setTargetAspectRatio(
-                    isDisplayPortrait ? ASPECT_RATIO_3_4 : ASPECT_RATIO_4_3);
+            mImageCaptureConfigBuilder.setTargetAspectRatio(AspectRatio.RATIO_4_3);
+            targetAspectRatio = isDisplayPortrait ? ASPECT_RATIO_3_4 : ASPECT_RATIO_4_3;
         } else {
-            mImageCaptureConfigBuilder.setTargetAspectRatio(
-                    isDisplayPortrait ? ASPECT_RATIO_9_16 : ASPECT_RATIO_16_9);
-            mPreviewConfigBuilder.setTargetAspectRatio(
-                    isDisplayPortrait ? ASPECT_RATIO_9_16 : ASPECT_RATIO_16_9);
+            mImageCaptureConfigBuilder.setTargetAspectRatio(AspectRatio.RATIO_16_9);
+            targetAspectRatio = isDisplayPortrait ? ASPECT_RATIO_9_16 : ASPECT_RATIO_16_9;
         }
 
         mImageCaptureConfigBuilder.setTargetRotation(getDisplaySurfaceRotation());
@@ -243,15 +240,9 @@
         mVideoCapture = new VideoCapture(mVideoCaptureConfigBuilder.build());
         mPreviewConfigBuilder.setLensFacing(mCameraLensFacing);
 
-        int relativeCameraOrientation = getRelativeCameraOrientation(false);
-
-        if (relativeCameraOrientation == 90 || relativeCameraOrientation == 270) {
-            mPreviewConfigBuilder.setTargetResolution(
-                    new Size(getMeasuredHeight(), getMeasuredWidth()));
-        } else {
-            mPreviewConfigBuilder.setTargetResolution(
-                    new Size(getMeasuredWidth(), getMeasuredHeight()));
-        }
+        // Adjusts the preview resolution according to the view size and the target aspect ratio.
+        int height = (int) (getMeasuredWidth() / targetAspectRatio.floatValue());
+        mPreviewConfigBuilder.setTargetResolution(new Size(getMeasuredWidth(), height));
 
         mPreview = new Preview(mPreviewConfigBuilder.build());
         mPreview.setOnPreviewOutputUpdateListener(
@@ -605,7 +596,7 @@
     // Update view related information used in use cases
     private void updateViewInfo() {
         if (mImageCapture != null) {
-            mImageCapture.setTargetAspectRatio(new Rational(getWidth(), getHeight()));
+            mImageCapture.setTargetAspectRatioCustom(new Rational(getWidth(), getHeight()));
             mImageCapture.setTargetRotation(getDisplaySurfaceRotation());
         }
 
diff --git a/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/CameraXActivity.java b/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/CameraXActivity.java
index 00d3d30..e8f9ecc 100644
--- a/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/CameraXActivity.java
+++ b/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/CameraXActivity.java
@@ -24,8 +24,6 @@
 import android.graphics.SurfaceTexture;
 import android.os.Bundle;
 import android.os.Environment;
-import android.os.Handler;
-import android.os.Looper;
 import android.os.StrictMode;
 import android.os.SystemClock;
 import android.util.Log;
@@ -396,7 +394,6 @@
                 new ImageAnalysisConfig.Builder()
                         .setLensFacing(mCurrentCameraLensFacing)
                         .setTargetName("ImageAnalysis")
-                        .setCallbackHandler(new Handler(Looper.getMainLooper()))
                         .build();
 
         mImageAnalysis = new ImageAnalysis(config);
@@ -409,6 +406,7 @@
         }
 
         mImageAnalysis.setAnalyzer(
+                CameraXExecutors.mainThreadExecutor(),
                 new ImageAnalysis.Analyzer() {
                     @Override
                     public void analyze(ImageProxy image, int rotationDegrees) {
diff --git a/camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/CameraExtensionsActivity.java b/camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/CameraExtensionsActivity.java
index aac9f9e..52d9f26 100644
--- a/camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/CameraExtensionsActivity.java
+++ b/camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/CameraExtensionsActivity.java
@@ -23,8 +23,6 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Environment;
-import android.os.Handler;
-import android.os.HandlerThread;
 import android.os.StrictMode;
 import android.util.Log;
 import android.view.TextureView;
@@ -89,8 +87,6 @@
     private ImageCapture mImageCapture;
     private ImageCaptureType mCurrentImageCaptureType = ImageCaptureType.IMAGE_CAPTURE_TYPE_HDR;
 
-    private HandlerThread mHandlerThread = new HandlerThread("CameraExtensionsActivityHandler");
-
     // Espresso testing variables
     @VisibleForTesting
     CountingIdlingResource mTakePictureIdlingResource = new CountingIdlingResource("TakePicture");
@@ -249,7 +245,6 @@
                         builder);
                 if (hdrImageCaptureExtender.isExtensionAvailable()) {
                     hdrImageCaptureExtender.enableExtension();
-                    builder.setCallbackHandler(new Handler(mHandlerThread.getLooper()));
                 }
                 break;
             case IMAGE_CAPTURE_TYPE_BOKEH:
@@ -394,8 +389,6 @@
                 new StrictMode.VmPolicy.Builder().detectAll().penaltyLog().build();
         StrictMode.setVmPolicy(policy);
 
-        mHandlerThread.start();
-
         // Get params from adb extra string
         Bundle bundle = getIntent().getExtras();
         if (bundle != null) {
@@ -416,12 +409,6 @@
         setupPermissions();
     }
 
-    @Override
-    protected void onDestroy() {
-        super.onDestroy();
-        mHandlerThread.quitSafely();
-    }
-
     private void setupCamera() {
         try {
             // Wait for permissions before proceeding.
diff --git a/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/AutoImageCaptureExtenderImpl.java b/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/AutoImageCaptureExtenderImpl.java
index 3d08d35..90b1f20 100755
--- a/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/AutoImageCaptureExtenderImpl.java
+++ b/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/AutoImageCaptureExtenderImpl.java
@@ -176,4 +176,9 @@
     public int getMaxCaptureStage() {
         return 3;
     }
+
+    @Override
+    public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+        return null;
+    }
 }
diff --git a/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/AutoPreviewExtenderImpl.java b/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/AutoPreviewExtenderImpl.java
index 6992fab..03e4331 100755
--- a/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/AutoPreviewExtenderImpl.java
+++ b/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/AutoPreviewExtenderImpl.java
@@ -18,10 +18,14 @@
 import android.content.Context;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CaptureRequest;
+import android.util.Pair;
+import android.util.Size;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
+import java.util.List;
+
 /**
  * Implementation for auto preview use case.
  *
@@ -68,6 +72,11 @@
     }
 
     @Override
+    public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+        return null;
+    }
+
+    @Override
     public void onInit(String cameraId, CameraCharacteristics cameraCharacteristics,
             Context context) {
 
diff --git a/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/BeautyImageCaptureExtenderImpl.java b/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/BeautyImageCaptureExtenderImpl.java
index 2c5bbdf..fb33c00 100755
--- a/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/BeautyImageCaptureExtenderImpl.java
+++ b/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/BeautyImageCaptureExtenderImpl.java
@@ -16,9 +16,11 @@
 package androidx.camera.extensions.impl;
 
 import android.content.Context;
+import android.graphics.ImageFormat;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.TotalCaptureResult;
+import android.hardware.camera2.params.StreamConfigurationMap;
 import android.media.Image;
 import android.media.ImageWriter;
 import android.os.Build;
@@ -45,12 +47,14 @@
     private static final String TAG = "BeautyICExtender";
     private static final int DEFAULT_STAGE_ID = 0;
     private static final int SESSION_STAGE_ID = 101;
+    private CameraCharacteristics mCameraCharacteristics;
 
     public BeautyImageCaptureExtenderImpl() {
     }
 
     @Override
     public void init(String cameraId, CameraCharacteristics cameraCharacteristics) {
+        mCameraCharacteristics = cameraCharacteristics;
     }
 
     @Override
@@ -176,4 +180,30 @@
     public int getMaxCaptureStage() {
         return 3;
     }
+
+    @Override
+    public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+        List<Pair<Integer, Size[]>> formatResolutionsPairList = new ArrayList<>();
+
+        StreamConfigurationMap map =
+                mCameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+
+        if (map != null) {
+            // The sample implementation only retrieves originally supported resolutions from
+            // CameraCharacteristics for JPEG and YUV_420_888 formats to return.
+            Size[] outputSizes = map.getOutputSizes(ImageFormat.JPEG);
+
+            if (outputSizes != null) {
+                formatResolutionsPairList.add(Pair.create(ImageFormat.JPEG, outputSizes));
+            }
+
+            outputSizes = map.getOutputSizes(ImageFormat.YUV_420_888);
+
+            if (outputSizes != null) {
+                formatResolutionsPairList.add(Pair.create(ImageFormat.YUV_420_888, outputSizes));
+            }
+        }
+
+        return formatResolutionsPairList;
+    }
 }
diff --git a/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/BeautyPreviewExtenderImpl.java b/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/BeautyPreviewExtenderImpl.java
index a5c511e..99a57f9 100755
--- a/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/BeautyPreviewExtenderImpl.java
+++ b/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/BeautyPreviewExtenderImpl.java
@@ -16,12 +16,19 @@
 package androidx.camera.extensions.impl;
 
 import android.content.Context;
+import android.graphics.ImageFormat;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.params.StreamConfigurationMap;
+import android.util.Pair;
+import android.util.Size;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * Implementation for beauty preview use case.
  *
@@ -31,12 +38,14 @@
 public final class BeautyPreviewExtenderImpl implements PreviewExtenderImpl {
     private static final int DEFAULT_STAGE_ID = 0;
     private static final int SESSION_STAGE_ID = 101;
+    private CameraCharacteristics mCameraCharacteristics;
 
     public BeautyPreviewExtenderImpl() {
     }
 
     @Override
     public void init(String cameraId, CameraCharacteristics cameraCharacteristics) {
+        mCameraCharacteristics = cameraCharacteristics;
     }
 
     @Override
@@ -68,6 +77,26 @@
     }
 
     @Override
+    public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+        List<Pair<Integer, Size[]>> formatResolutionsPairList = new ArrayList<>();
+
+        StreamConfigurationMap map =
+                mCameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+
+        if (map != null) {
+            // The sample implementation only retrieves originally supported resolutions from
+            // CameraCharacteristics for PRIVATE format to return.
+            Size[] outputSizes = map.getOutputSizes(ImageFormat.PRIVATE);
+
+            if (outputSizes != null) {
+                formatResolutionsPairList.add(Pair.create(ImageFormat.PRIVATE, outputSizes));
+            }
+        }
+
+        return formatResolutionsPairList;
+    }
+
+    @Override
     public void onInit(String cameraId, CameraCharacteristics cameraCharacteristics,
             Context context) {
 
diff --git a/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/BokehImageCaptureExtenderImpl.java b/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/BokehImageCaptureExtenderImpl.java
index 2da14ff..d6dd45f 100644
--- a/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/BokehImageCaptureExtenderImpl.java
+++ b/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/BokehImageCaptureExtenderImpl.java
@@ -178,4 +178,9 @@
         return 3;
     }
 
+    @Override
+    public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+        return null;
+    }
+
 }
diff --git a/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/BokehPreviewExtenderImpl.java b/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/BokehPreviewExtenderImpl.java
index 4fa72b7..94a142e 100644
--- a/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/BokehPreviewExtenderImpl.java
+++ b/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/BokehPreviewExtenderImpl.java
@@ -19,12 +19,15 @@
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.TotalCaptureResult;
+import android.util.Pair;
 import android.util.Size;
 import android.view.Surface;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
+import java.util.List;
+
 /**
  * Implementation for bokeh preview use case.
  *
@@ -106,6 +109,11 @@
     }
 
     @Override
+    public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+        return null;
+    }
+
+    @Override
     public void onInit(String cameraId, CameraCharacteristics cameraCharacteristics,
             Context context) {
 
diff --git a/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/HdrImageCaptureExtenderImpl.java b/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/HdrImageCaptureExtenderImpl.java
index 39f961a..8a2d01b 100644
--- a/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/HdrImageCaptureExtenderImpl.java
+++ b/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/HdrImageCaptureExtenderImpl.java
@@ -201,4 +201,9 @@
         return 4;
     }
 
+    @Override
+    public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+        return null;
+    }
+
 }
diff --git a/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/HdrPreviewExtenderImpl.java b/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/HdrPreviewExtenderImpl.java
index f85518b..acf0f7e 100644
--- a/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/HdrPreviewExtenderImpl.java
+++ b/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/HdrPreviewExtenderImpl.java
@@ -20,12 +20,15 @@
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.TotalCaptureResult;
 import android.media.Image;
+import android.util.Pair;
 import android.util.Size;
 import android.view.Surface;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
+import java.util.List;
+
 /**
  * Implementation for HDR preview use case.
  *
@@ -69,6 +72,11 @@
         return mProcessor;
     }
 
+    @Override
+    public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+        return null;
+    }
+
     private PreviewImageProcessorImpl mProcessor = new PreviewImageProcessorImpl() {
         Surface mSurface;
         Size mSize;
diff --git a/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/ImageCaptureExtenderImpl.java b/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/ImageCaptureExtenderImpl.java
index 8bf39ae..a844969 100644
--- a/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/ImageCaptureExtenderImpl.java
+++ b/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/ImageCaptureExtenderImpl.java
@@ -16,7 +16,12 @@
 
 package androidx.camera.extensions.impl;
 
+import android.graphics.ImageFormat;
 import android.hardware.camera2.CameraCharacteristics;
+import android.util.Pair;
+import android.util.Size;
+
+import androidx.annotation.Nullable;
 
 import java.util.List;
 
@@ -57,5 +62,21 @@
      * @return the maximum count.
      */
     int getMaxCaptureStage();
+
+    /**
+     * Returns the customized supported resolutions.
+     *
+     * <p>Pair list composed with {@link ImageFormat} and {@link Size} array will be returned.
+     *
+     * <p>The returned resolutions should be subset of the supported sizes retrieved from
+     * {@link android.hardware.camera2.params.StreamConfigurationMap} for the camera device. If the
+     * returned list is not null, it will be used to find the best resolutions combination for
+     * the bound use cases.
+     *
+     * @return the customized supported resolutions.
+     * @since 1.1
+     */
+    @Nullable
+    List<Pair<Integer, Size[]>> getSupportedResolutions();
 }
 
diff --git a/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/NightImageCaptureExtenderImpl.java b/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/NightImageCaptureExtenderImpl.java
index 83cc4f1..fe6daea 100755
--- a/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/NightImageCaptureExtenderImpl.java
+++ b/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/NightImageCaptureExtenderImpl.java
@@ -176,4 +176,9 @@
     public int getMaxCaptureStage() {
         return 3;
     }
+
+    @Override
+    public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+        return null;
+    }
 }
diff --git a/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/NightPreviewExtenderImpl.java b/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/NightPreviewExtenderImpl.java
index 3b85e7a..7c32035 100755
--- a/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/NightPreviewExtenderImpl.java
+++ b/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/NightPreviewExtenderImpl.java
@@ -18,10 +18,14 @@
 import android.content.Context;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CaptureRequest;
+import android.util.Pair;
+import android.util.Size;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
+import java.util.List;
+
 /**
  * Implementation for night preview use case.
  *
@@ -68,6 +72,11 @@
     }
 
     @Override
+    public List<Pair<Integer, Size[]>> getSupportedResolutions() {
+        return null;
+    }
+
+    @Override
     public void onInit(String cameraId, CameraCharacteristics cameraCharacteristics,
             Context context) {
 
diff --git a/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/PreviewExtenderImpl.java b/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/PreviewExtenderImpl.java
index cfe2995..d690da3 100644
--- a/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/PreviewExtenderImpl.java
+++ b/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/PreviewExtenderImpl.java
@@ -16,8 +16,15 @@
 
 package androidx.camera.extensions.impl;
 
+import android.graphics.ImageFormat;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.TotalCaptureResult;
+import android.util.Pair;
+import android.util.Size;
+
+import androidx.annotation.Nullable;
+
+import java.util.List;
 
 /**
  * Provides abstract methods that the OEM needs to implement to enable extensions in the preview.
@@ -76,4 +83,20 @@
      *
      */
     ProcessorImpl getProcessor();
+
+    /**
+     * Returns the customized supported resolutions.
+     *
+     * <p>Pair list composed with {@link ImageFormat} and {@link Size} array will be returned.
+     *
+     * <p>The returned resolutions should be subset of the supported sizes retrieved from
+     * {@link android.hardware.camera2.params.StreamConfigurationMap} for the camera device. If the
+     * returned list is not null, it will be used to find the best resolutions combination for
+     * the bound use cases.
+     *
+     * @return the customized supported resolutions.
+     * @since 1.1
+     */
+    @Nullable
+    List<Pair<Integer, Size[]>> getSupportedResolutions();
 }
diff --git a/car/core/api/1.0.0-alpha8.txt b/car/core/api/1.0.0-alpha8.txt
index 7966547..e6852f0 100644
--- a/car/core/api/1.0.0-alpha8.txt
+++ b/car/core/api/1.0.0-alpha8.txt
@@ -586,6 +586,7 @@
     method public void setDownButtonIcon(android.graphics.drawable.Drawable!);
     method public void setDownEnabled(boolean);
     method public void setOnAlphaJumpListener(androidx.car.widget.PagedScrollBarView.OnAlphaJumpListener?);
+    method public void setOnVisibilityChangedListener(androidx.car.widget.PagedScrollBarView.OnVisibilityChangedListener?);
     method public void setPaginationListener(androidx.car.widget.PagedScrollBarView.PaginationListener?);
     method public void setParameters(@IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int, boolean);
     method public void setScrollbarThumbColor(@ColorRes int);
@@ -598,6 +599,10 @@
     method public void onAlphaJump();
   }
 
+  public static interface PagedScrollBarView.OnVisibilityChangedListener {
+    method public void onVisibilityChanged(android.view.View, @androidx.car.widget.PagedScrollBarView.Visibility int);
+  }
+
   public static interface PagedScrollBarView.PaginationListener {
     method @Deprecated public default void onAlphaJump();
     method public void onPaginate(int);
@@ -605,6 +610,9 @@
     field public static final int PAGE_UP = 0; // 0x0
   }
 
+  @IntDef({android.view.View.VISIBLE, android.view.View.INVISIBLE, android.view.View.GONE}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface PagedScrollBarView.Visibility {
+  }
+
   public final class RadioButtonListItem extends androidx.car.widget.CompoundButtonListItem<androidx.car.widget.RadioButtonListItem.ViewHolder> {
     ctor public RadioButtonListItem(android.content.Context);
     method public static androidx.car.widget.RadioButtonListItem.ViewHolder createViewHolder(android.view.View);
diff --git a/car/core/api/current.txt b/car/core/api/current.txt
index 7966547..e6852f0 100644
--- a/car/core/api/current.txt
+++ b/car/core/api/current.txt
@@ -586,6 +586,7 @@
     method public void setDownButtonIcon(android.graphics.drawable.Drawable!);
     method public void setDownEnabled(boolean);
     method public void setOnAlphaJumpListener(androidx.car.widget.PagedScrollBarView.OnAlphaJumpListener?);
+    method public void setOnVisibilityChangedListener(androidx.car.widget.PagedScrollBarView.OnVisibilityChangedListener?);
     method public void setPaginationListener(androidx.car.widget.PagedScrollBarView.PaginationListener?);
     method public void setParameters(@IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int, boolean);
     method public void setScrollbarThumbColor(@ColorRes int);
@@ -598,6 +599,10 @@
     method public void onAlphaJump();
   }
 
+  public static interface PagedScrollBarView.OnVisibilityChangedListener {
+    method public void onVisibilityChanged(android.view.View, @androidx.car.widget.PagedScrollBarView.Visibility int);
+  }
+
   public static interface PagedScrollBarView.PaginationListener {
     method @Deprecated public default void onAlphaJump();
     method public void onPaginate(int);
@@ -605,6 +610,9 @@
     field public static final int PAGE_UP = 0; // 0x0
   }
 
+  @IntDef({android.view.View.VISIBLE, android.view.View.INVISIBLE, android.view.View.GONE}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface PagedScrollBarView.Visibility {
+  }
+
   public final class RadioButtonListItem extends androidx.car.widget.CompoundButtonListItem<androidx.car.widget.RadioButtonListItem.ViewHolder> {
     ctor public RadioButtonListItem(android.content.Context);
     method public static androidx.car.widget.RadioButtonListItem.ViewHolder createViewHolder(android.view.View);
diff --git a/car/core/api/public_plus_experimental_1.0.0-alpha8.txt b/car/core/api/public_plus_experimental_1.0.0-alpha8.txt
index 7966547..e6852f0 100644
--- a/car/core/api/public_plus_experimental_1.0.0-alpha8.txt
+++ b/car/core/api/public_plus_experimental_1.0.0-alpha8.txt
@@ -586,6 +586,7 @@
     method public void setDownButtonIcon(android.graphics.drawable.Drawable!);
     method public void setDownEnabled(boolean);
     method public void setOnAlphaJumpListener(androidx.car.widget.PagedScrollBarView.OnAlphaJumpListener?);
+    method public void setOnVisibilityChangedListener(androidx.car.widget.PagedScrollBarView.OnVisibilityChangedListener?);
     method public void setPaginationListener(androidx.car.widget.PagedScrollBarView.PaginationListener?);
     method public void setParameters(@IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int, boolean);
     method public void setScrollbarThumbColor(@ColorRes int);
@@ -598,6 +599,10 @@
     method public void onAlphaJump();
   }
 
+  public static interface PagedScrollBarView.OnVisibilityChangedListener {
+    method public void onVisibilityChanged(android.view.View, @androidx.car.widget.PagedScrollBarView.Visibility int);
+  }
+
   public static interface PagedScrollBarView.PaginationListener {
     method @Deprecated public default void onAlphaJump();
     method public void onPaginate(int);
@@ -605,6 +610,9 @@
     field public static final int PAGE_UP = 0; // 0x0
   }
 
+  @IntDef({android.view.View.VISIBLE, android.view.View.INVISIBLE, android.view.View.GONE}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface PagedScrollBarView.Visibility {
+  }
+
   public final class RadioButtonListItem extends androidx.car.widget.CompoundButtonListItem<androidx.car.widget.RadioButtonListItem.ViewHolder> {
     ctor public RadioButtonListItem(android.content.Context);
     method public static androidx.car.widget.RadioButtonListItem.ViewHolder createViewHolder(android.view.View);
diff --git a/car/core/api/public_plus_experimental_current.txt b/car/core/api/public_plus_experimental_current.txt
index 7966547..e6852f0 100644
--- a/car/core/api/public_plus_experimental_current.txt
+++ b/car/core/api/public_plus_experimental_current.txt
@@ -586,6 +586,7 @@
     method public void setDownButtonIcon(android.graphics.drawable.Drawable!);
     method public void setDownEnabled(boolean);
     method public void setOnAlphaJumpListener(androidx.car.widget.PagedScrollBarView.OnAlphaJumpListener?);
+    method public void setOnVisibilityChangedListener(androidx.car.widget.PagedScrollBarView.OnVisibilityChangedListener?);
     method public void setPaginationListener(androidx.car.widget.PagedScrollBarView.PaginationListener?);
     method public void setParameters(@IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int, boolean);
     method public void setScrollbarThumbColor(@ColorRes int);
@@ -598,6 +599,10 @@
     method public void onAlphaJump();
   }
 
+  public static interface PagedScrollBarView.OnVisibilityChangedListener {
+    method public void onVisibilityChanged(android.view.View, @androidx.car.widget.PagedScrollBarView.Visibility int);
+  }
+
   public static interface PagedScrollBarView.PaginationListener {
     method @Deprecated public default void onAlphaJump();
     method public void onPaginate(int);
@@ -605,6 +610,9 @@
     field public static final int PAGE_UP = 0; // 0x0
   }
 
+  @IntDef({android.view.View.VISIBLE, android.view.View.INVISIBLE, android.view.View.GONE}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface PagedScrollBarView.Visibility {
+  }
+
   public final class RadioButtonListItem extends androidx.car.widget.CompoundButtonListItem<androidx.car.widget.RadioButtonListItem.ViewHolder> {
     ctor public RadioButtonListItem(android.content.Context);
     method public static androidx.car.widget.RadioButtonListItem.ViewHolder createViewHolder(android.view.View);
diff --git a/car/core/api/restricted_1.0.0-alpha8.txt b/car/core/api/restricted_1.0.0-alpha8.txt
index 05c3757..ee527c2 100644
--- a/car/core/api/restricted_1.0.0-alpha8.txt
+++ b/car/core/api/restricted_1.0.0-alpha8.txt
@@ -611,6 +611,7 @@
     method public void setDownButtonIcon(android.graphics.drawable.Drawable!);
     method public void setDownEnabled(boolean);
     method public void setOnAlphaJumpListener(androidx.car.widget.PagedScrollBarView.OnAlphaJumpListener?);
+    method public void setOnVisibilityChangedListener(androidx.car.widget.PagedScrollBarView.OnVisibilityChangedListener?);
     method public void setPaginationListener(androidx.car.widget.PagedScrollBarView.PaginationListener?);
     method public void setParameters(@IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int, boolean);
     method public void setScrollbarThumbColor(@ColorRes int);
@@ -623,6 +624,10 @@
     method public void onAlphaJump();
   }
 
+  public static interface PagedScrollBarView.OnVisibilityChangedListener {
+    method public void onVisibilityChanged(android.view.View, @androidx.car.widget.PagedScrollBarView.Visibility int);
+  }
+
   public static interface PagedScrollBarView.PaginationListener {
     method @Deprecated public default void onAlphaJump();
     method public void onPaginate(int);
@@ -630,6 +635,9 @@
     field public static final int PAGE_UP = 0; // 0x0
   }
 
+  @IntDef({android.view.View.VISIBLE, android.view.View.INVISIBLE, android.view.View.GONE}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface PagedScrollBarView.Visibility {
+  }
+
   public final class RadioButtonListItem extends androidx.car.widget.CompoundButtonListItem<androidx.car.widget.RadioButtonListItem.ViewHolder> {
     ctor public RadioButtonListItem(android.content.Context);
     method public static androidx.car.widget.RadioButtonListItem.ViewHolder createViewHolder(android.view.View);
diff --git a/car/core/api/restricted_current.txt b/car/core/api/restricted_current.txt
index 05c3757..ee527c2 100644
--- a/car/core/api/restricted_current.txt
+++ b/car/core/api/restricted_current.txt
@@ -611,6 +611,7 @@
     method public void setDownButtonIcon(android.graphics.drawable.Drawable!);
     method public void setDownEnabled(boolean);
     method public void setOnAlphaJumpListener(androidx.car.widget.PagedScrollBarView.OnAlphaJumpListener?);
+    method public void setOnVisibilityChangedListener(androidx.car.widget.PagedScrollBarView.OnVisibilityChangedListener?);
     method public void setPaginationListener(androidx.car.widget.PagedScrollBarView.PaginationListener?);
     method public void setParameters(@IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int, boolean);
     method public void setScrollbarThumbColor(@ColorRes int);
@@ -623,6 +624,10 @@
     method public void onAlphaJump();
   }
 
+  public static interface PagedScrollBarView.OnVisibilityChangedListener {
+    method public void onVisibilityChanged(android.view.View, @androidx.car.widget.PagedScrollBarView.Visibility int);
+  }
+
   public static interface PagedScrollBarView.PaginationListener {
     method @Deprecated public default void onAlphaJump();
     method public void onPaginate(int);
@@ -630,6 +635,9 @@
     field public static final int PAGE_UP = 0; // 0x0
   }
 
+  @IntDef({android.view.View.VISIBLE, android.view.View.INVISIBLE, android.view.View.GONE}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface PagedScrollBarView.Visibility {
+  }
+
   public final class RadioButtonListItem extends androidx.car.widget.CompoundButtonListItem<androidx.car.widget.RadioButtonListItem.ViewHolder> {
     ctor public RadioButtonListItem(android.content.Context);
     method public static androidx.car.widget.RadioButtonListItem.ViewHolder createViewHolder(android.view.View);
diff --git a/car/core/src/main/java/androidx/car/widget/PagedListView.java b/car/core/src/main/java/androidx/car/widget/PagedListView.java
index bacdb30..1aa0efa 100644
--- a/car/core/src/main/java/androidx/car/widget/PagedListView.java
+++ b/car/core/src/main/java/androidx/car/widget/PagedListView.java
@@ -373,7 +373,8 @@
                     a.getInt(R.styleable.PagedListView_scrollBarGravity, Gravity.LEFT);
         }
 
-        mScrollBarView.setVisibility(mIsScrollBarVisibleIfNeeded ? VISIBLE : GONE);
+        // Initially hide scrollbar view and make it visible when required.
+        mScrollBarView.setVisibility(GONE);
 
         if (mIsScrollBarVisibleIfNeeded) {
             // Use the top margin that is defined in the layout as the default value.
@@ -412,6 +413,9 @@
             setGutter(Gutter.BOTH);
         }
 
+        // Upon scrollbar visibility changes, margins for gutters may need to be updated.
+        mScrollBarView.setOnVisibilityChangedListener(
+                (scrollbar, visibility) -> setGutter(mGutter));
         a.recycle();
     }
 
@@ -467,16 +471,18 @@
         boolean isLTR = ViewCompat.getLayoutDirection(this) == ViewCompat.LAYOUT_DIRECTION_LTR;
         boolean isScrollBarGravityStart =
                 (scrollBarGravity == Gravity.START)
-                        || (scrollBarGravity == Gravity.LEFT && isLTR)
-                        || (scrollBarGravity == Gravity.RIGHT && !isLTR);
+                        || ((scrollBarGravity == Gravity.LEFT) && isLTR)
+                        || ((scrollBarGravity == Gravity.RIGHT) && !isLTR);
         boolean isScrollBarGravityEnd =
                 (scrollBarGravity == Gravity.END)
-                        || (scrollBarGravity == Gravity.RIGHT && isLTR)
-                        || (scrollBarGravity == Gravity.LEFT && !isLTR);
+                        || ((scrollBarGravity == Gravity.RIGHT) && isLTR)
+                        || ((scrollBarGravity == Gravity.LEFT) && !isLTR);
+
+        boolean isScrollbarVisible = mScrollBarView.getVisibility() == View.VISIBLE;
 
         // Default starting margin is either the width of the scroll bar, if present at the start,
         // or just flush to the edge.
-        int startMargin = mIsScrollBarVisibleIfNeeded && isScrollBarGravityStart
+        int startMargin = isScrollbarVisible && isScrollBarGravityStart
                 ? mScrollBarView.getLayoutParams().width : 0;
         if ((mGutter & Gutter.START) != 0) {
             // Ensure that the gutter value is large enough so that the RecyclerView does not
@@ -486,7 +492,7 @@
 
         // Default end margin is either the width of the scroll bar, if present at the end, or
         // just flush to the edge.
-        int endMargin = mIsScrollBarVisibleIfNeeded && isScrollBarGravityEnd
+        int endMargin = isScrollbarVisible && isScrollBarGravityEnd
                 ? mScrollBarView.getLayoutParams().width : 0;
         if ((mGutter & Gutter.END) != 0) {
             // Ensure that the gutter value is large enough so that the RecyclerView does not
@@ -504,6 +510,12 @@
         // If there's a gutter, set ClipToPadding to false so that CardView's shadow will still
         // appear outside of the padding.
         mRecyclerView.setClipToPadding(startMargin == 0 && endMargin == 0);
+
+        // If currently performing a layout pass, updated margins may not get applied until
+        // the next layout pass. Schedule another layout pass to ensure margins are updated.
+        if (isInLayout()) {
+            post(this::requestLayout);
+        }
     }
 
     /**
@@ -1262,7 +1274,7 @@
         boolean isAtEnd = isAtEnd();
 
         if ((isAtStart && isAtEnd) || layoutManager.getItemCount() == 0) {
-            mScrollBarView.setVisibility(View.INVISIBLE);
+            mScrollBarView.setVisibility(View.GONE);
             return;
         }
 
@@ -1304,7 +1316,7 @@
         RecyclerView.LayoutManager layoutManager = mRecyclerView.getLayoutManager();
 
         if ((isAtStart && isAtEnd) || layoutManager == null || layoutManager.getItemCount() == 0) {
-            mScrollBarView.setVisibility(View.INVISIBLE);
+            mScrollBarView.setVisibility(View.GONE);
         } else {
             mScrollBarView.setVisibility(VISIBLE);
         }
diff --git a/car/core/src/main/java/androidx/car/widget/PagedScrollBarView.java b/car/core/src/main/java/androidx/car/widget/PagedScrollBarView.java
index fa47350..ac4e393 100644
--- a/car/core/src/main/java/androidx/car/widget/PagedScrollBarView.java
+++ b/car/core/src/main/java/androidx/car/widget/PagedScrollBarView.java
@@ -33,11 +33,16 @@
 
 import androidx.annotation.ColorRes;
 import androidx.annotation.DrawableRes;
+import androidx.annotation.IntDef;
 import androidx.annotation.IntRange;
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.car.R;
 import androidx.core.content.ContextCompat;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /** A custom view to provide list scroll behaviour -- up/down buttons and scroll indicator. */
 public class PagedScrollBarView extends ViewGroup {
     private static final float BUTTON_DISABLED_ALPHA = 0.2f;
@@ -63,7 +68,8 @@
          * @deprecated Use {@link OnAlphaJumpListener#onAlphaJump()} instead.
          */
         @Deprecated
-        default void onAlphaJump() {}
+        default void onAlphaJump() {
+        }
     }
 
     public interface OnAlphaJumpListener {
@@ -79,6 +85,24 @@
         void onAlphaJump();
     }
 
+    @IntDef({View.VISIBLE, View.INVISIBLE, View.GONE})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Visibility {
+    }
+
+    /** Listener for changes to the visibility of the scrollbar view. */
+    public interface OnVisibilityChangedListener {
+
+        /**
+         * Called when the visibility of the scrollbar view changes.
+         *
+         * @param view       A reference to the scrollbar for which the visibility changed.
+         * @param visibility The new visibility, one of {@code View.VISIBLE}, {@code View
+         * .INVISIBLE} or {@code View.GONE}
+         */
+        void onVisibilityChanged(@NonNull View view, @Visibility int visibility);
+    }
+
     private final ImageView mUpButton;
     private final PaginateButtonClickListener mUpButtonClickListener;
     private final ImageView mDownButton;
@@ -86,6 +110,7 @@
     private final TextView mAlphaJumpButton;
     private final AlphaJumpButtonClickListener mAlphaJumpButtonClickListener;
     private final View mScrollThumb;
+    private OnVisibilityChangedListener mOnVisibilityChangedListener;
 
     private final int mSeparatingMargin;
     private final int mScrollBarThumbWidth;
@@ -198,6 +223,22 @@
         mAlphaJumpButtonClickListener.setOnAlphaJumpListener(listener);
     }
 
+    /**
+     * Sets the listener that will be notified when the visibility of the scrollbar changes.
+     *
+     * @param listener The listener to set.
+     */
+    public void setOnVisibilityChangedListener(@Nullable OnVisibilityChangedListener listener) {
+        mOnVisibilityChangedListener = listener;
+    }
+
+    @Override
+    protected void onVisibilityChanged(@NonNull View changedView, int visibility) {
+        if (mOnVisibilityChangedListener != null) {
+            mOnVisibilityChangedListener.onVisibilityChanged(this, visibility);
+        }
+    }
+
     /** Returns {@code true} if the "up" button is pressed */
     public boolean isUpPressed() {
         return mUpButton.isPressed();
diff --git a/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/FcsCodegenTests.kt b/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/FcsCodegenTests.kt
index 186259f..2a91d2a 100644
--- a/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/FcsCodegenTests.kt
+++ b/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/FcsCodegenTests.kt
@@ -2361,14 +2361,6 @@
         super.setUp()
     }
 
-    override fun setupEnvironment(environment: KotlinCoreEnvironment) {
-        environment.configuration.put(
-            ComposeConfigurationKeys.COMPOSABLE_CHECKER_MODE_KEY,
-            ComposableAnnotationChecker.Mode.FCS
-        )
-        super.setupEnvironment(environment)
-    }
-
     private var isSetup = false
     private inline fun <T> ensureSetup(block: () -> T): T {
         if (!isSetup) setUp()
diff --git a/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/FcsModelCodeGenTests.kt b/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/FcsModelCodeGenTests.kt
index 8ce6c9e..5597c44 100644
--- a/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/FcsModelCodeGenTests.kt
+++ b/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/FcsModelCodeGenTests.kt
@@ -335,14 +335,6 @@
             advanceMethod.invoke(instanceOfClass, *arguments)
         }
     }
-
-    override fun setupEnvironment(environment: KotlinCoreEnvironment) {
-        environment.configuration.put(
-            ComposeConfigurationKeys.COMPOSABLE_CHECKER_MODE_KEY,
-            ComposableAnnotationChecker.Mode.FCS
-        )
-        super.setupEnvironment(environment)
-    }
 }
 
 private val Activity.root get() = findViewById(ROOT_ID) as ViewGroup
diff --git a/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/analysis/ComposableCheckerTests.kt b/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/analysis/ComposableCheckerTests.kt
index e40e8fd..b951372 100644
--- a/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/analysis/ComposableCheckerTests.kt
+++ b/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/analysis/ComposableCheckerTests.kt
@@ -4,8 +4,6 @@
 import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
 import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoots
 import androidx.compose.plugins.kotlin.AbstractComposeDiagnosticsTest
-import androidx.compose.plugins.kotlin.ComposableAnnotationChecker.Mode
-import androidx.compose.plugins.kotlin.ComposeConfigurationKeys
 import androidx.compose.plugins.kotlin.newConfiguration
 import com.intellij.openapi.util.Disposer
 
@@ -26,12 +24,11 @@
         )
     }
 
-    fun doTest(mode: Mode, text: String, expectPass: Boolean) {
+    fun doTest(text: String, expectPass: Boolean) {
         val disposable = TestDisposable()
         val classPath = createClasspath()
         val configuration = newConfiguration()
         configuration.addJvmClasspathRoots(classPath)
-        configuration.put(ComposeConfigurationKeys.COMPOSABLE_CHECKER_MODE_KEY, mode)
 
         val environment =
             KotlinCoreEnvironment.createForTests(
@@ -45,26 +42,26 @@
             doTest(text, environment)
             if (!expectPass) {
                 throw Exception(
-                    "Test unexpectedly passed when running in $mode mode, but SHOULD FAIL"
+                    "Test unexpectedly passed, but SHOULD FAIL"
                 )
             }
         } catch (e: Exception) {
-            if (expectPass) throw Exception("Unexpected failure while running in $mode mode", e)
+            if (expectPass) throw Exception(e)
         } finally {
             Disposer.dispose(disposable)
         }
     }
 
-    fun doTest(modes: Int, expectedText: String) {
-        doTest(Mode.KTX_CHECKED, expectedText, (modes and MODE_KTX_CHECKED) != 0)
-        doTest(Mode.KTX_STRICT, expectedText, (modes and MODE_KTX_STRICT) != 0)
-        doTest(Mode.KTX_PEDANTIC, expectedText, (modes and MODE_KTX_PEDANTIC) != 0)
-        doTest(Mode.FCS, expectedText, (modes and MODE_FCS) != 0)
+    fun check(expectedText: String) {
+        doTest(expectedText, true)
+    }
+
+    fun checkFail(expectedText: String) {
+        doTest(expectedText, false)
     }
 
     fun testComposableReporting001() {
-        doTest(
-            MODE_KTX_CHECKED, """
+        checkFail("""
             import androidx.compose.*;
             import android.widget.TextView;
 
@@ -76,8 +73,7 @@
                 <myStatelessFunctionalComponent />
             }
         """)
-        doTest(
-            MODE_KTX_CHECKED or MODE_KTX_STRICT or MODE_KTX_PEDANTIC or MODE_FCS, """
+        check("""
             import androidx.compose.*;
             import android.widget.TextView;
 
@@ -94,16 +90,14 @@
     }
 
     fun testComposableReporting002() {
-        doTest(
-            MODE_KTX_CHECKED, """
+        checkFail("""
             import androidx.compose.*;
             import android.widget.TextView;
 
             val myLambda1 = { <TextView text="Hello World!" /> }
             val myLambda2: ()->Unit = { <TextView text="Hello World!" /> }
         """)
-        doTest(
-            MODE_KTX_CHECKED or MODE_KTX_STRICT or MODE_KTX_PEDANTIC or MODE_FCS, """
+        check("""
             import androidx.compose.*;
             import android.widget.TextView;
 
@@ -113,8 +107,7 @@
     }
 
     fun testComposableReporting003() {
-        doTest(
-            MODE_KTX_STRICT or MODE_KTX_PEDANTIC or MODE_FCS, """
+        check("""
             import androidx.compose.*;
             import android.widget.TextView;
 
@@ -125,8 +118,7 @@
     }
 
     fun testComposableReporting004() {
-        doTest(
-            MODE_KTX_STRICT or MODE_KTX_PEDANTIC or MODE_FCS, """
+        check("""
             import androidx.compose.*;
             import android.widget.TextView;
 
@@ -139,8 +131,7 @@
     }
 
     fun testComposableReporting005() {
-        doTest(
-            MODE_KTX_CHECKED or MODE_KTX_STRICT or MODE_KTX_PEDANTIC or MODE_FCS, """
+        check("""
             import androidx.compose.*;
             import android.widget.TextView;
 
@@ -153,8 +144,7 @@
     }
 
     fun testComposableReporting006() {
-        doTest(
-            MODE_KTX_CHECKED, """
+        checkFail("""
             import androidx.compose.*;
             import android.widget.TextView;
 
@@ -166,8 +156,7 @@
                 System.out.println(bar)
             }
         """)
-        doTest(
-            MODE_KTX_CHECKED or MODE_KTX_STRICT or MODE_KTX_PEDANTIC or MODE_FCS, """
+        check("""
             import androidx.compose.*;
             import android.widget.TextView;
 
@@ -183,8 +172,7 @@
     }
 
     fun testComposableReporting007() {
-        doTest(
-            MODE_KTX_CHECKED or MODE_KTX_STRICT or MODE_KTX_PEDANTIC, """
+        checkFail("""
             import androidx.compose.*;
             import android.widget.TextView;
 
@@ -195,8 +183,7 @@
     }
 
     fun testComposableReporting008() {
-        doTest(
-            MODE_KTX_CHECKED or MODE_KTX_STRICT or MODE_KTX_PEDANTIC, """
+        checkFail("""
             import androidx.compose.*;
             import android.widget.TextView;
 
@@ -211,8 +198,7 @@
     }
 
     fun testComposableReporting009() {
-        doTest(
-            MODE_KTX_CHECKED, """
+        checkFail("""
             import androidx.compose.*;
             import android.widget.TextView;
 
@@ -224,8 +210,7 @@
                 <!SVC_INVOCATION!>myStatelessFunctionalComponent<!>()
             }
         """)
-        doTest(
-            MODE_KTX_CHECKED or MODE_KTX_STRICT or MODE_KTX_PEDANTIC, """
+        checkFail("""
             import androidx.compose.*;
             import android.widget.TextView;
 
@@ -241,8 +226,7 @@
     }
 
     fun testComposableReporting010() {
-        doTest(
-            MODE_KTX_CHECKED or MODE_KTX_STRICT or MODE_KTX_PEDANTIC or MODE_FCS, """
+        check("""
             import androidx.compose.*;
             import android.widget.TextView;
 
@@ -258,8 +242,7 @@
     }
 
     fun testComposableReporting011() {
-        doTest(
-            MODE_KTX_CHECKED or MODE_KTX_STRICT or MODE_KTX_PEDANTIC, """
+        checkFail("""
             import androidx.compose.*;
             import android.widget.TextView;
 
@@ -273,8 +256,7 @@
     }
 
     fun testComposableReporting012() {
-        doTest(
-            MODE_KTX_CHECKED or MODE_KTX_STRICT or MODE_KTX_PEDANTIC, """
+        checkFail("""
             import androidx.compose.*;
             import android.widget.TextView;
 
@@ -288,8 +270,7 @@
     }
 
     fun testComposableReporting013() {
-        doTest(
-            MODE_KTX_CHECKED or MODE_KTX_STRICT or MODE_KTX_PEDANTIC or MODE_FCS, """
+        check("""
             import androidx.compose.*;
             import android.widget.TextView;
 
@@ -303,8 +284,7 @@
     }
 
     fun testComposableReporting014() {
-        doTest(
-            MODE_KTX_CHECKED or MODE_KTX_STRICT or MODE_KTX_PEDANTIC or MODE_FCS, """
+        check("""
             import androidx.compose.*;
             import android.widget.TextView;
 
@@ -320,8 +300,7 @@
     }
 
     fun testComposableReporting015() {
-        doTest(
-            MODE_KTX_CHECKED, """
+        checkFail("""
             import androidx.compose.*;
             import android.widget.TextView;
 
@@ -341,8 +320,7 @@
                 }
             }
         """)
-        doTest(
-            MODE_KTX_STRICT or MODE_KTX_PEDANTIC or MODE_FCS, """
+        check("""
             import androidx.compose.*;
             import android.widget.TextView;
 
@@ -365,16 +343,14 @@
     }
 
     fun testComposableReporting016() {
-        doTest(
-            MODE_KTX_CHECKED or MODE_KTX_STRICT or MODE_KTX_PEDANTIC or MODE_FCS, """
+        check("""
             import androidx.compose.*;
 
             <!WRONG_ANNOTATION_TARGET!>@Composable<!>
             class Noise() {}
         """)
 
-        doTest(
-            MODE_KTX_CHECKED or MODE_KTX_STRICT or MODE_KTX_PEDANTIC or MODE_FCS, """
+        check("""
             import androidx.compose.*;
 
             val adHoc = <!WRONG_ANNOTATION_TARGET!>@Composable()<!> object {
@@ -383,8 +359,7 @@
             }
         """)
 
-        doTest(
-            MODE_KTX_CHECKED or MODE_KTX_STRICT or MODE_KTX_PEDANTIC or MODE_FCS, """
+        check("""
             import androidx.compose.*;
 
             open class Noise() {}
@@ -397,8 +372,7 @@
     }
 
     fun testComposableReporting017() {
-        doTest(
-            MODE_KTX_CHECKED, """
+        checkFail("""
             import androidx.compose.*;
             import android.widget.LinearLayout;
             import android.widget.TextView;
@@ -413,8 +387,7 @@
                 <Foo><TextView text="Hello" /></Foo>
             }
         """)
-        doTest(
-            MODE_KTX_STRICT or MODE_KTX_PEDANTIC or MODE_FCS, """
+        check("""
             import androidx.compose.*;
             import android.widget.LinearLayout;
             import android.widget.TextView;
@@ -432,8 +405,7 @@
     }
 
     fun testComposableReporting018() {
-        doTest(
-            MODE_KTX_CHECKED or MODE_KTX_STRICT, """
+        checkFail("""
             import androidx.compose.*;
             import android.widget.TextView;
 
@@ -442,8 +414,7 @@
                 System.out.println(myVariable)
             }
         """)
-        doTest(
-            MODE_KTX_PEDANTIC or MODE_FCS, """
+        check("""
             import androidx.compose.*;
             import android.widget.TextView;
 
@@ -455,8 +426,7 @@
     }
 
     fun testComposableReporting019() {
-        doTest(
-            MODE_KTX_CHECKED or MODE_KTX_STRICT, """
+        checkFail("""
            import androidx.compose.*;
            import android.widget.TextView;
 
@@ -467,8 +437,7 @@
                System.out.println(myVariable)
            }
         """)
-        doTest(
-            MODE_KTX_PEDANTIC or MODE_FCS, """
+        check("""
             import androidx.compose.*;
             import android.widget.TextView;
 
@@ -482,8 +451,7 @@
     }
 
     fun testComposableReporting020() {
-        doTest(
-            MODE_KTX_CHECKED or MODE_KTX_STRICT or MODE_KTX_PEDANTIC or MODE_FCS, """
+        check("""
             import androidx.compose.*;
             import android.widget.TextView;
 
@@ -497,8 +465,7 @@
     }
 
     fun testComposableReporting021() {
-        doTest(
-            MODE_KTX_CHECKED or MODE_KTX_STRICT or MODE_KTX_PEDANTIC or MODE_FCS, """
+        check("""
             import androidx.compose.*;
             import android.widget.TextView;
 
@@ -511,8 +478,7 @@
     }
 
     fun testComposableReporting022() {
-        doTest(
-            MODE_KTX_CHECKED, """
+        checkFail("""
             import androidx.compose.*;
             import android.widget.TextView;
 
@@ -521,8 +487,7 @@
                 myList.forEach { value: Int -> <TextView text=value.toString() />; System.out.println(value); }
             }
         """)
-        doTest(
-            MODE_KTX_PEDANTIC or MODE_FCS, """
+        check("""
             import androidx.compose.*;
             import android.widget.TextView;
 
@@ -534,8 +499,7 @@
     }
 
     fun testComposableReporting023() {
-        doTest(
-            MODE_KTX_CHECKED or MODE_KTX_STRICT or MODE_KTX_PEDANTIC or MODE_FCS, """
+        check("""
                import androidx.compose.*;
                import android.widget.TextView;
 
@@ -549,8 +513,7 @@
     }
 
     fun testComposableReporting024() {
-        doTest(
-            MODE_KTX_CHECKED or MODE_KTX_STRICT or MODE_KTX_PEDANTIC or MODE_FCS, """
+        check("""
             import androidx.compose.*;
             import android.widget.TextView;
             import android.widget.LinearLayout;
@@ -562,8 +525,7 @@
     }
 
     fun testComposableReporting025() {
-        doTest(
-            MODE_KTX_CHECKED or MODE_KTX_STRICT or MODE_KTX_PEDANTIC or MODE_FCS, """
+        check("""
             import androidx.compose.*;
             import android.widget.TextView;
 
@@ -575,8 +537,7 @@
     }
 
     fun testComposableReporting026() {
-        doTest(
-            MODE_KTX_CHECKED or MODE_KTX_STRICT or MODE_KTX_PEDANTIC or MODE_FCS, """
+        check("""
             import androidx.compose.*;
             import android.widget.LinearLayout;
             import android.widget.TextView;
@@ -591,8 +552,7 @@
     }
 
     fun testComposableReporting027() {
-        doTest(
-            MODE_KTX_CHECKED or MODE_KTX_STRICT or MODE_KTX_PEDANTIC or MODE_FCS, """
+        check("""
             import androidx.compose.*;
             import android.widget.LinearLayout;
             import android.widget.TextView;
@@ -609,8 +569,7 @@
     }
 
     fun testComposableReporting028() {
-        doTest(
-            MODE_KTX_CHECKED or MODE_KTX_STRICT, """
+        checkFail("""
             import androidx.compose.*;
             import android.widget.TextView;
 
@@ -619,8 +578,7 @@
                 myVariable()
             }
         """)
-        doTest(
-            MODE_KTX_PEDANTIC or MODE_FCS, """
+        check("""
             import androidx.compose.*;
             import android.widget.TextView;
 
@@ -632,8 +590,7 @@
     }
 
     fun testComposableReporting029() {
-        doTest(
-            MODE_KTX_CHECKED or MODE_KTX_STRICT, """
+        checkFail("""
             import androidx.compose.*;
             import android.widget.TextView;
 
@@ -643,8 +600,7 @@
                 <myVariable />
             }
         """)
-        doTest(
-            MODE_KTX_PEDANTIC or MODE_FCS, """
+        check("""
             import androidx.compose.*;
             import android.widget.TextView;
 
@@ -657,8 +613,7 @@
     }
 
     fun testComposableReporting030() {
-        doTest(
-            MODE_KTX_CHECKED or MODE_KTX_STRICT or MODE_KTX_PEDANTIC or MODE_FCS, """
+        check("""
             import androidx.compose.*;
             import android.widget.TextView;
 
@@ -671,8 +626,7 @@
     }
 
     fun testComposableReporting031() {
-        doTest(
-            MODE_KTX_STRICT or MODE_KTX_PEDANTIC or MODE_FCS, """
+        check("""
             import androidx.compose.*;
             import android.widget.TextView;
 
@@ -684,8 +638,7 @@
     }
 
     fun testComposableReporting032() {
-        doTest(
-            MODE_KTX_CHECKED or MODE_KTX_STRICT or MODE_KTX_PEDANTIC or MODE_FCS, """
+        check("""
             import androidx.compose.*;
             import androidx.compose.Children;
             import android.widget.TextView;
@@ -701,8 +654,7 @@
     }
 
     fun testComposableReporting033() {
-        doTest(
-            MODE_KTX_CHECKED or MODE_KTX_STRICT or MODE_KTX_PEDANTIC or MODE_FCS, """
+        check("""
             import androidx.compose.*;
             import androidx.compose.Children;
             import android.widget.TextView;
@@ -718,8 +670,7 @@
     }
 
     fun testComposableReporting034() {
-        doTest(
-            MODE_KTX_CHECKED or MODE_KTX_STRICT, """
+        checkFail("""
             import androidx.compose.*;
             import android.widget.TextView;
 
@@ -731,8 +682,7 @@
                 <f2 />
             }
         """)
-        doTest(
-            MODE_KTX_PEDANTIC or MODE_FCS, """
+        check("""
             import androidx.compose.*;
             import android.widget.TextView;
 
@@ -747,8 +697,7 @@
     }
 
     fun testComposableReporting035() {
-        doTest(
-            MODE_KTX_PEDANTIC or MODE_FCS, """
+        check("""
             import androidx.compose.*
 
             @Composable
@@ -760,8 +709,7 @@
     }
 
     fun testComposableReporting036() {
-        doTest(
-            MODE_KTX_CHECKED, """
+        checkFail("""
             import androidx.compose.*
             import android.widget.TextView;
 
@@ -775,8 +723,7 @@
                 <!SVC_INVOCATION!>Foo<!>()
             }
         """)
-        doTest(
-            MODE_KTX_CHECKED, """
+        checkFail("""
             import androidx.compose.*
             import android.widget.TextView;
 
@@ -793,8 +740,7 @@
     }
 
     fun testComposableReporting037() {
-        doTest(
-            MODE_KTX_CHECKED, """
+        checkFail("""
             import androidx.compose.*
             import android.widget.TextView;
 
@@ -811,8 +757,7 @@
     }
 
     fun testComposableReporting038() {
-        doTest(
-            MODE_KTX_STRICT or MODE_KTX_PEDANTIC or MODE_FCS, """
+        check("""
             import androidx.compose.*
             import android.widget.TextView;
 
@@ -828,8 +773,7 @@
     }
 
     fun testComposableReporting039() {
-        doTest(
-            MODE_KTX_CHECKED, """
+        checkFail("""
             import androidx.compose.*
             import android.widget.TextView;
 
@@ -845,8 +789,7 @@
                 Foo()
             }
         """)
-        doTest(
-            MODE_KTX_CHECKED, """
+        checkFail("""
             import androidx.compose.*
             import android.widget.TextView;
 
@@ -865,8 +808,7 @@
     }
 
     fun testComposableReporting040() {
-        doTest(
-            MODE_KTX_CHECKED or MODE_KTX_STRICT or MODE_KTX_PEDANTIC or MODE_FCS, """
+        check("""
             import androidx.compose.*
             import android.widget.TextView;
 
@@ -884,8 +826,7 @@
     }
 
     fun testComposableReporting041() {
-        doTest(
-            MODE_KTX_CHECKED or MODE_KTX_STRICT or MODE_KTX_PEDANTIC or MODE_FCS, """
+        check("""
             import androidx.compose.*
             import android.widget.TextView;
 
@@ -905,8 +846,7 @@
     }
 
     fun testComposableReporting042() {
-        doTest(
-            MODE_FCS, """
+        check("""
             import androidx.compose.*
             import android.widget.TextView;
 
@@ -925,8 +865,7 @@
                 Foo()
             }
         """)
-        doTest(
-            MODE_FCS, """
+        check("""
             import androidx.compose.*
             import android.widget.TextView;
 
@@ -944,8 +883,7 @@
     }
 
     fun testComposableReporting043() {
-        doTest(
-            MODE_FCS, """
+        check("""
             import androidx.compose.*
             import android.widget.TextView;
 
@@ -961,8 +899,7 @@
     }
 
     fun testComposableReporting044() {
-        doTest(
-            MODE_FCS, """
+        check("""
             import androidx.compose.*
             import android.widget.TextView;
 
@@ -979,8 +916,7 @@
     }
 
     fun testComposableReporting045() {
-        doTest(
-            MODE_FCS, """
+        check("""
             import androidx.compose.*;
 
             @Composable
@@ -993,8 +929,7 @@
     }
 
     fun testComposableReporting046() {
-        doTest(
-            MODE_FCS, """
+        check( """
             import androidx.compose.*;
             import android.widget.TextView;
 
diff --git a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposableAnnotationChecker.kt b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposableAnnotationChecker.kt
index 198d33a..aa1d342 100644
--- a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposableAnnotationChecker.kt
+++ b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposableAnnotationChecker.kt
@@ -81,9 +81,7 @@
 import org.jetbrains.kotlin.types.upperIfFlexible
 import org.jetbrains.kotlin.util.OperatorNameConventions
 
-open class ComposableAnnotationChecker(
-    val nullableMode: Mode? = null
-) : CallChecker, DeclarationChecker,
+open class ComposableAnnotationChecker : CallChecker, DeclarationChecker,
     AdditionalTypeChecker, AdditionalAnnotationChecker, StorageComponentContainerContributor {
 
     enum class Mode {
@@ -98,9 +96,6 @@
     }
 
     companion object {
-        val DEFAULT_MODE =
-            Mode.FCS
-
         fun get(project: Project): ComposableAnnotationChecker {
             return StorageComponentContainerContributor.getInstances(project).single {
                 it is ComposableAnnotationChecker
@@ -149,11 +144,6 @@
         return candidateDescriptor.hasComposableAnnotation()
     }
 
-    open fun getMode(psi: PsiElement): Mode {
-        if (nullableMode != null) return nullableMode
-        return DEFAULT_MODE
-    }
-
     fun analyze(trace: BindingTrace, descriptor: FunctionDescriptor): Composability {
         val unwrappedDescriptor =
             if (descriptor is
@@ -203,7 +193,6 @@
         signatureComposability: Composability
     ): Composability {
         var composability = signatureComposability
-        val mode = getMode(element)
         var localKtx = false
         var localFcs = false
         var isInlineLambda = false
@@ -230,20 +219,18 @@
             }
 
             override fun visitCallExpression(expression: KtCallExpression) {
-                if (mode == Mode.FCS) {
-                    // Can be null in cases where the call isn't resolvable (eg. due to a bug/typo in the user's code)
-                    val isCallComposable = trace.get(FCS_RESOLVEDCALL_COMPOSABLE, expression)
-                    if (isCallComposable == true) {
-                        localFcs = true
-                        if (!isInlineLambda && composability != Composability.MARKED) {
-                            // Report error on composable element to make it obvious which invocation is offensive
-                            val reportElement = expression.calleeExpression ?: expression
-                            trace.reportFromPlugin(
-                                ComposeErrors.COMPOSABLE_INVOCATION_IN_NON_COMPOSABLE
-                                    .on(reportElement),
-                                ComposeDefaultErrorMessages
-                            )
-                        }
+                // Can be null in cases where the call isn't resolvable (eg. due to a bug/typo in the user's code)
+                val isCallComposable = trace.get(FCS_RESOLVEDCALL_COMPOSABLE, expression)
+                if (isCallComposable == true) {
+                    localFcs = true
+                    if (!isInlineLambda && composability != Composability.MARKED) {
+                        // Report error on composable element to make it obvious which invocation is offensive
+                        val reportElement = expression.calleeExpression ?: expression
+                        trace.reportFromPlugin(
+                            ComposeErrors.COMPOSABLE_INVOCATION_IN_NON_COMPOSABLE
+                                .on(reportElement),
+                            ComposeDefaultErrorMessages
+                        )
                     }
                 }
                 super.visitCallExpression(expression)
@@ -251,8 +238,7 @@
         }, null)
         if (
             (localKtx || localFcs) &&
-            !isInlineLambda && composability != Composability.MARKED &&
-            (mode == Mode.KTX_STRICT || mode == Mode.KTX_PEDANTIC || mode == Mode.FCS)
+            !isInlineLambda && composability != Composability.MARKED
         ) {
             val reportElement = when (element) {
                 is KtNamedFunction -> element.nameIdentifier ?: element
@@ -438,33 +424,12 @@
         reportOn: PsiElement,
         context: CallCheckerContext
     ) {
-        val mode = getMode(reportOn)
         val shouldBeTag = shouldInvokeAsTag(context.trace, resolvedCall)
-        if (mode == Mode.FCS) {
-            context.trace.record(
-                FCS_RESOLVEDCALL_COMPOSABLE,
-                resolvedCall.call.callElement,
-                shouldBeTag
-            )
-        }
-        // NOTE: we return early here because the KtxCallResolver does its own composable checking (by delegating to this class) and has
-        // more context to make the right call.
-        if (reportOn.parent is KtxElement) return
-        if (
-            mode != Mode.FCS && context.resolutionContext is CallResolutionContext &&
-            resolvedCall is ResolvedCallImpl &&
-            (context.resolutionContext as CallResolutionContext).call.callElement is
-                    KtCallExpression
-        ) {
-            if (shouldBeTag) {
-                context.trace.reportFromPlugin(
-                    ComposeErrors.SVC_INVOCATION.on(
-                        reportOn as KtElement,
-                        resolvedCall.candidateDescriptor.name.asString()
-                    ), ComposeDefaultErrorMessages
-                )
-            }
-        }
+        context.trace.record(
+            FCS_RESOLVEDCALL_COMPOSABLE,
+            resolvedCall.call.callElement,
+            shouldBeTag
+        )
     }
 
     override fun checkType(
@@ -473,8 +438,6 @@
         expressionTypeWithSmartCast: KotlinType,
         c: ResolutionContext<*>
     ) {
-        val mode = getMode(expression)
-        if (mode != Mode.KTX_PEDANTIC && mode != Mode.FCS) return
         if (expression is KtLambdaExpression) {
             val expectedType = c.expectedType
             if (expectedType === TypeUtils.NO_EXPECTED_TYPE) return
diff --git a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposePlugin.kt b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposePlugin.kt
index 807cdb4..b6e21a3 100644
--- a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposePlugin.kt
+++ b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposePlugin.kt
@@ -33,33 +33,19 @@
 import org.jetbrains.kotlin.extensions.TypeResolutionInterceptorExtension
 import org.jetbrains.kotlin.parsing.KtxParsingExtension
 import org.jetbrains.kotlin.psi2ir.extensions.SyntheticIrExtension
-import androidx.compose.plugins.kotlin.ComposeConfigurationKeys.COMPOSABLE_CHECKER_MODE_KEY
 import androidx.compose.plugins.kotlin.frames.analysis.FrameModelChecker
 import androidx.compose.plugins.kotlin.frames.analysis.FramePackageAnalysisHandlerExtension
 import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisHandlerExtension
 
-object ComposeConfigurationKeys {
-    val COMPOSABLE_CHECKER_MODE_KEY = CompilerConfigurationKey<ComposableAnnotationChecker.Mode>(
-        "@composable checker mode"
-    )
-}
-
 class ComposeCommandLineProcessor : CommandLineProcessor {
 
     companion object {
         val PLUGIN_ID = "androidx.compose.plugins.kotlin"
-        val SYNTAX_OPTION = CliOption(
-            "syntax",
-            "<ktx_checked|ktx_strict|ktx_pedantic|fcs>",
-            "@composable syntax checker mode",
-            required = false,
-            allowMultipleOccurrences = false
-        )
     }
 
     override val pluginId =
         PLUGIN_ID
-    override val pluginOptions = listOf(SYNTAX_OPTION)
+    override val pluginOptions = emptyList<CliOption>()
 
     @Suppress("OverridingDeprecatedMember")
     override fun processOption(
@@ -73,13 +59,7 @@
         option: AbstractCliOption,
         value: String,
         configuration: CompilerConfiguration
-    ) = when (option) {
-        SYNTAX_OPTION -> configuration.put(
-            COMPOSABLE_CHECKER_MODE_KEY,
-            ComposableAnnotationChecker.Mode.valueOf(value.toUpperCase())
-        )
-        else -> throw CliOptionProcessingException("Unknown option: ${option.optionName}")
-    }
+    ) = throw CliOptionProcessingException("Unknown option: ${option.optionName}")
 }
 
 class ComposeComponentRegistrar : ComponentRegistrar {
@@ -95,6 +75,7 @@
 
     companion object {
 
+        @Suppress("UNUSED_PARAMETER")
         fun registerProjectExtensions(project: Project, configuration: CompilerConfiguration) {
             StorageComponentContainerContributor.registerExtension(
                 project,
@@ -102,12 +83,7 @@
             )
             StorageComponentContainerContributor.registerExtension(
                 project,
-                ComposableAnnotationChecker(
-                    configuration.get(
-                        COMPOSABLE_CHECKER_MODE_KEY,
-                        ComposableAnnotationChecker.DEFAULT_MODE
-                    )
-                )
+                ComposableAnnotationChecker()
             )
             StorageComponentContainerContributor.registerExtension(
                 project,
diff --git a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/compiler/lower/ComposeObservePatcher.kt b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/compiler/lower/ComposeObservePatcher.kt
index 1a608ed..339a001 100644
--- a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/compiler/lower/ComposeObservePatcher.kt
+++ b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/compiler/lower/ComposeObservePatcher.kt
@@ -413,7 +413,7 @@
                 context.state.bindingContext, "tmp for composable analysis"
             )
         val composability =
-            ComposableAnnotationChecker(ComposableAnnotationChecker.Mode.KTX_CHECKED)
+            ComposableAnnotationChecker()
                 .analyze(
                 tmpTrace,
                 declaration.descriptor
diff --git a/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/ComposableAnnotationChecker.kt b/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/ComposableAnnotationChecker.kt
deleted file mode 100644
index 3814444..0000000
--- a/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/ComposableAnnotationChecker.kt
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright 2019 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.compose.plugins.idea
-
-import androidx.compose.plugins.kotlin.ComposableAnnotationChecker
-import com.intellij.openapi.module.ModuleUtilCore
-import com.intellij.psi.PsiElement
-
-class ComposableAnnotationChecker() : ComposableAnnotationChecker() {
-    override fun getMode(psi: PsiElement): Mode {
-        if (nullableMode != null) return nullableMode
-        val module = ModuleUtilCore.findModuleForPsiElement(psi) ?: return DEFAULT_MODE
-        val kotlinFacet = org.jetbrains.kotlin.idea.facet.KotlinFacet.get(module)
-                ?: return DEFAULT_MODE
-        val commonArgs = kotlinFacet.configuration.settings.compilerArguments ?: return DEFAULT_MODE
-        val modeOption = commonArgs.pluginOptions?.firstOrNull {
-            it.startsWith("plugin:androidx.compose.plugins.kotlin:syntax=")
-        } ?: return DEFAULT_MODE
-        return Mode.valueOf(
-                modeOption.substring(modeOption.indexOf("=") + 1).toUpperCase()
-        )
-    }
-}
diff --git a/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/conversion/AndroidConversion.kt b/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/conversion/AndroidConversion.kt
deleted file mode 100644
index b8aa00d9..0000000
--- a/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/conversion/AndroidConversion.kt
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright 2018 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.compose.plugins.idea.conversion
-
-import org.jetbrains.kotlin.builtins.DefaultBuiltIns
-import org.jetbrains.kotlin.j2k.ast.Expression
-import org.jetbrains.kotlin.j2k.ast.LiteralExpression
-import org.jetbrains.kotlin.j2k.ast.QualifiedExpression
-import org.jetbrains.kotlin.name.FqName
-import androidx.compose.plugins.kotlin.ComposeUtils
-
-internal val ANDROID_CONVERSION = conversion {
-    DefaultBuiltIns.Instance.booleanType
-    anyClass {
-        anyAttribute {
-            "true" { success<Boolean>(LiteralExpression("true")) }
-            "false" { success<Boolean>(LiteralExpression("false")) }
-        }
-    }
-    "android.view.View" {
-        "focusable" {
-            anyValue {
-                val enumValue = when (xmlValue) {
-                    "auto" -> "FOCUSABLE_AUTO"
-                    "true" -> "FOCUSABLE"
-                    "false" -> "NOT_FOCUSABLE"
-                    else -> null
-                }
-
-                if (enumValue != null) {
-                    success<Int>(enumValue.asIdentifier("android.view.View.$enumValue"))
-                } else {
-                    failure()
-                }
-            }
-        }
-        anyOf("layout_width", "layout_height") {
-            anyValue {
-                val enumValue = when (xmlValue) {
-                    "match_parent", "fill_parent" -> "MATCH_PARENT"
-                    "wrap_content" -> "WRAP_CONTENT"
-                    else -> null
-                }
-
-                if (enumValue != null) {
-                    success<Int>(
-                        enumValue.asIdentifier(
-                            FqName("android.view.ViewGroup.LayoutParams.$enumValue")
-                        )
-                    )
-                } else {
-                    failure()
-                }
-            }
-        }
-        anyAttribute {
-            // Resource.
-            val resourceTypes = listOf(
-                "anim", "animator", "array", "attr", "bool", "color", "dimen", "drawable", "font",
-                "id", "integer", "layout", "menu", "mipmap", "string", "style", "styleable"
-            ).joinToString("|")
-            pattern("^@(([^:]+):)?($resourceTypes)/(.+)$") { matcher ->
-                success<Int>(with(matcher) {
-                    resourceExpression(
-                        group(2),
-                        group(3),
-                        group(4)
-                    )
-                })
-            }
-
-            // Dimension.
-            pattern("^(\\d+(\\.\\d+)?)(px|dip|dp|sp|pt|in|mm)$") { matcher ->
-                // Returns expression: <value>.<unit>
-                val unit = matcher.group(3)
-                success(
-                    QualifiedExpression(
-                        qualifier = LiteralExpression(matcher.group(1)),
-                        identifier = unit.asIdentifier(
-                            ComposeUtils.composeFqName("adapters.$unit")
-                        ),
-                        dotPrototype = null
-                    ), ComposeUtils.composeFqName("adapters.Dimension")
-                )
-            }
-        }
-    }
-}
-
-// Returns expression: [<package>.]R.<type>.<name>
-private fun resourceExpression(packageName: String?, type: String, name: String): Expression {
-    // TODO(jdemeulenaere): We directly return the resource ID
-    return if (packageName == null) {
-        resourceExpression(type, name)
-    } else {
-        QualifiedExpression(
-            qualifier = packageName.asIdentifier(),
-            identifier = resourceExpression(type, name),
-            dotPrototype = null
-        )
-    }
-}
-
-// Returns expression: R.<type>.<name>
-private fun resourceExpression(type: String, name: String): Expression {
-    return QualifiedExpression(
-        qualifier = QualifiedExpression(
-            qualifier = "R".asIdentifier(),
-            identifier = type.asIdentifier(),
-            dotPrototype = null
-        ),
-        identifier = name.asIdentifier(),
-        dotPrototype = null
-    )
-}
\ No newline at end of file
diff --git a/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/conversion/Conversion.kt b/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/conversion/Conversion.kt
deleted file mode 100644
index 6b575fdbb..0000000
--- a/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/conversion/Conversion.kt
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * Copyright 2018 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.compose.plugins.idea.conversion
-
-import org.jetbrains.kotlin.j2k.ast.Expression
-import org.jetbrains.kotlin.j2k.ast.Identifier
-import org.jetbrains.kotlin.name.FqName
-import java.util.regex.Matcher
-import java.util.regex.Pattern
-
-internal fun conversion(builder: ConversionConfiguration.() -> Unit): ConversionConfiguration {
-    return ConversionConfiguration().apply(builder)
-}
-
-@DslMarker
-annotation class ConversionDsl
-
-@ConversionDsl
-class ConversionConfiguration {
-    val classConversions = arrayListOf<ClassConversion>()
-
-    fun anyClass(builder: AnyClassConversion.() -> Unit) {
-        classConversions.add(AnyClassConversion().apply(builder))
-    }
-
-    operator fun String.invoke(builder: ExactClassConversion.() -> Unit) {
-        classConversions.add(ExactClassConversion(this).apply(builder))
-    }
-}
-
-@ConversionDsl
-sealed class ClassConversion {
-    val attributeConversions = arrayListOf<AttributeConversion>()
-
-    abstract fun matchesAny(classNames: Set<String>): Boolean
-
-    fun anyAttribute(builder: AnyAttributeConversion.() -> Unit) {
-        attributeConversions.add(AnyAttributeConversion().apply(builder))
-    }
-
-    fun anyOf(vararg attributes: String, builder: ExactXmlAttributeConversion.() -> Unit) {
-        attributes.forEach { attribute ->
-            attributeConversions.add(
-                ExactXmlAttributeConversion(
-                    attribute
-                ).apply(builder))
-        }
-    }
-
-    operator fun String.invoke(builder: ExactXmlAttributeConversion.() -> Unit) {
-        attributeConversions.add(
-            ExactXmlAttributeConversion(
-                this
-            ).apply(builder))
-    }
-}
-class AnyClassConversion : ClassConversion() {
-    override fun matchesAny(classNames: Set<String>): Boolean = true
-}
-class ExactClassConversion(val className: String) : ClassConversion() {
-    override fun matchesAny(classNames: Set<String>): Boolean = classNames.contains(className)
-}
-
-interface ConversionContext {
-    // TODO(jdemeulenaere): Add tagClass here if a converter needs it.
-    val xmlName: String
-    val xmlValue: String
-}
-
-sealed class ConversionResult {
-    class Success(
-        val expression: Expression,
-        val kotlinType: FqName
-    ) : ConversionResult()
-
-    object Failure : ConversionResult()
-}
-
-inline fun <reified T> success(expression: Expression) =
-    ConversionResult.Success(
-        expression,
-        FqName(T::class.qualifiedName!!)
-    )
-fun success(expression: Expression, kotlinType: FqName) =
-    ConversionResult.Success(expression, kotlinType)
-fun failure() = ConversionResult.Failure
-
-typealias Conversion = ConversionContext.() -> ConversionResult
-typealias ConversionWithMatcher = ConversionContext.(Matcher) -> ConversionResult
-
-@ConversionDsl
-sealed class AttributeConversion {
-    val valueConversions = arrayListOf<ValueConversion>()
-
-    abstract fun matches(xmlName: String): Boolean
-
-    fun anyValue(conversion: Conversion) {
-        valueConversions.add(AnyValueConversion(conversion))
-    }
-
-    fun anyOf(vararg values: String, conversion: Conversion) {
-        values.forEach { value ->
-            valueConversions.add(
-                ExactValueConversion(
-                    value,
-                    conversion
-                )
-            )
-        }
-    }
-
-    operator fun String.invoke(conversion: Conversion) {
-        valueConversions.add(
-            ExactValueConversion(
-                this,
-                conversion
-            )
-        )
-    }
-
-    fun pattern(pattern: String, conversion: ConversionWithMatcher) {
-        valueConversions.add(
-            PatternValueConversion(
-                pattern,
-                conversion
-            )
-        )
-    }
-}
-class AnyAttributeConversion : AttributeConversion() {
-    override fun matches(xmlName: String) = true
-}
-class ExactXmlAttributeConversion(private val attributeName: String) : AttributeConversion() {
-    override fun matches(xmlName: String): Boolean = this.attributeName == xmlName
-}
-
-abstract class ValueConversion {
-    abstract fun matches(attributeValue: String): Boolean
-
-    abstract fun convert(context: ConversionContext): ConversionResult
-}
-
-abstract class BaseValueConversion(private val conversion: Conversion) : ValueConversion() {
-    override fun convert(context: ConversionContext): ConversionResult = conversion(context)
-}
-
-class AnyValueConversion(conversion: Conversion) : BaseValueConversion(conversion) {
-    override fun matches(attributeValue: String): Boolean = true
-}
-class ExactValueConversion(
-    private val value: String,
-    conversion: Conversion
-) : BaseValueConversion(conversion) {
-    override fun matches(attributeValue: String): Boolean = this.value == attributeValue
-}
-
-class PatternValueConversion(
-    pattern: String,
-    private val conversion: ConversionWithMatcher
-) : ValueConversion() {
-    private val pattern = Pattern.compile(pattern)
-    override fun matches(attributeValue: String): Boolean =
-        pattern.matcher(attributeValue).matches()
-
-    override fun convert(context: ConversionContext): ConversionResult {
-        return context.conversion(pattern.matcher(context.xmlValue).also { assert(it.matches()) })
-    }
-}
-
-internal fun String.asIdentifier(import: FqName? = null) =
-    Identifier(this, isNullable = false, imports = import?.let(::listOf) ?: emptyList())
-internal fun String.asIdentifier(import: String): Identifier =
-    Identifier(this, isNullable = false, imports = listOf(FqName(import)))
diff --git a/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/conversion/ConversionUtils.kt b/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/conversion/ConversionUtils.kt
deleted file mode 100644
index 581f6ea..0000000
--- a/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/conversion/ConversionUtils.kt
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright 2018 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.compose.plugins.idea.conversion
-
-import org.jetbrains.kotlin.idea.caches.resolve.resolveImportReference
-import org.jetbrains.kotlin.idea.util.ImportInsertHelper
-import org.jetbrains.kotlin.idea.util.application.runWriteAction
-import org.jetbrains.kotlin.name.FqName
-import org.jetbrains.kotlin.psi.KtFile
-import androidx.compose.plugins.kotlin.ComposeUtils
-
-internal fun createFunctionalComponent(
-    name: String,
-    composeBody: String,
-    imports: MutableSet<FqName>
-): String {
-    // TODO(jdemeulenaere): Allow to specify package.
-    // We don't use org.jetbrains.kotlin.j2k.ast.Function because it requires a j2k.Converter instance to create a DeferredElement
-    // (the type of the Function body).
-
-    imports.add(ComposeUtils.composeFqName("Composable"))
-    return """
-            |@Composable
-            |fun $name() {
-            |    $composeBody
-            |}""".trimMargin()
-}
-
-internal fun addComposeStarImport(targetFile: KtFile) {
-    runWriteAction {
-        targetFile.resolveImportReference(
-            ComposeUtils.composeFqName("Composable")
-        ).firstOrNull()?.let {
-            ImportInsertHelper.getInstance(targetFile.project).importDescriptor(
-                targetFile,
-                it,
-                forceAllUnderImport = true
-            )
-        }
-    }
-}
\ No newline at end of file
diff --git a/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/conversion/ConvertXmlCopyPasteProcessor.kt b/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/conversion/ConvertXmlCopyPasteProcessor.kt
deleted file mode 100644
index 208c016..0000000
--- a/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/conversion/ConvertXmlCopyPasteProcessor.kt
+++ /dev/null
@@ -1,422 +0,0 @@
-/*
- * Copyright 2018 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.compose.plugins.idea.conversion
-
-import com.intellij.codeInsight.editorActions.CopyPastePostProcessor
-import com.intellij.codeInsight.editorActions.TextBlockTransferableData
-import com.intellij.lang.xml.XMLLanguage
-import com.intellij.openapi.diagnostic.Logger
-import com.intellij.openapi.editor.Editor
-import com.intellij.openapi.editor.RangeMarker
-import com.intellij.openapi.project.DumbService
-import com.intellij.openapi.project.Project
-import com.intellij.openapi.util.Ref
-import com.intellij.openapi.util.TextRange
-import com.intellij.psi.PsiDocumentManager
-import com.intellij.psi.PsiElement
-import com.intellij.psi.PsiErrorElement
-import com.intellij.psi.PsiFile
-import com.intellij.psi.PsiFileFactory
-import com.intellij.psi.impl.source.tree.TreeElement
-import com.intellij.psi.xml.XmlFile
-import com.intellij.psi.xml.XmlTag
-import org.jetbrains.kotlin.idea.caches.resolve.resolveImportReference
-import org.jetbrains.kotlin.idea.conversion.copy.CopiedKotlinCode
-import org.jetbrains.kotlin.idea.conversion.copy.ElementAndTextList
-import org.jetbrains.kotlin.idea.conversion.copy.ElementsAndTextsProcessor
-import org.jetbrains.kotlin.idea.j2k.EmptyDocCommentConverter
-import org.jetbrains.kotlin.idea.util.ImportInsertHelper
-import org.jetbrains.kotlin.idea.util.addAnnotation
-import org.jetbrains.kotlin.idea.util.application.runWriteAction
-import org.jetbrains.kotlin.idea.util.findAnnotation
-import org.jetbrains.kotlin.j2k.CodeBuilder
-import org.jetbrains.kotlin.lexer.KtTokens
-import org.jetbrains.kotlin.name.FqName
-import org.jetbrains.kotlin.psi.KtCallableDeclaration
-import org.jetbrains.kotlin.psi.KtFile
-import org.jetbrains.kotlin.psi.KtNamedFunction
-import org.jetbrains.kotlin.psi.KtxElement
-import org.jetbrains.kotlin.psi.psiUtil.anyDescendantOfType
-import org.jetbrains.kotlin.psi.psiUtil.elementsInRange
-import org.jetbrains.kotlin.psi.psiUtil.startOffset
-import androidx.compose.plugins.kotlin.ComposeUtils
-import androidx.compose.plugins.idea.editor.KtxEditorOptions
-import androidx.compose.plugins.idea.parentOfType
-import org.jetbrains.kotlin.idea.core.util.end
-import org.jetbrains.kotlin.idea.core.util.range
-import org.jetbrains.kotlin.idea.core.util.start
-import java.awt.datatransfer.DataFlavor
-import java.awt.datatransfer.Transferable
-
-class ConvertXmlCopyPasteProcessor : CopyPastePostProcessor<TextBlockTransferableData>() {
-    private val LOG = Logger.getInstance(ConvertXmlCopyPasteProcessor::class.java)
-
-    private class CopiedXmlCode(
-        val fileText: String,
-        val startOffsets: IntArray,
-        val endOffsets: IntArray
-    ) : TextBlockTransferableData {
-        override fun getFlavor() =
-            DATA_FLAVOR
-        override fun getOffsetCount() = 0
-
-        override fun getOffsets(offsets: IntArray?, index: Int) = index
-        override fun setOffsets(offsets: IntArray?, index: Int) = index
-
-        companion object {
-            val DATA_FLAVOR: DataFlavor = DataFlavor(
-                CopiedXmlCode::class.java,
-                "class: ConvertXmlCopyPasteProcessor.CopiedXmlCode"
-            )
-        }
-    }
-
-    private class CopiedText(val text: String) : TextBlockTransferableData {
-        override fun getFlavor() =
-            DATA_FLAVOR
-        override fun getOffsetCount() = 0
-
-        override fun getOffsets(offsets: IntArray?, index: Int) = index
-        override fun setOffsets(offsets: IntArray?, index: Int) = index
-
-        companion object {
-            val DATA_FLAVOR: DataFlavor = DataFlavor(
-                CopiedText::class.java,
-                "class: ConvertXmlCopyPasteProcessor.CopiedText"
-            )
-        }
-    }
-
-    override fun collectTransferableData(
-        file: PsiFile,
-        editor: Editor,
-        startOffsets: IntArray,
-        endOffsets: IntArray
-    ): List<TextBlockTransferableData> {
-        if (file !is XmlFile) return emptyList()
-        return listOf(
-            CopiedXmlCode(
-                file.text,
-                startOffsets,
-                endOffsets
-            )
-        )
-    }
-
-    override fun extractTransferableData(content: Transferable): List<TextBlockTransferableData> {
-        try {
-            when {
-                // Do not try to convert text copied from Kotlin file.
-                content.isDataFlavorSupported(CopiedKotlinCode.DATA_FLAVOR) -> return emptyList()
-                content.isDataFlavorSupported(CopiedXmlCode.DATA_FLAVOR) -> {
-                    return listOf(content.getTransferData(CopiedXmlCode.DATA_FLAVOR)
-                            as TextBlockTransferableData)
-                }
-                content.isDataFlavorSupported(DataFlavor.stringFlavor) -> {
-                    val text = content.getTransferData(DataFlavor.stringFlavor) as String
-                    return listOf(
-                        CopiedText(
-                            text
-                        )
-                    )
-                }
-            }
-        } catch (e: Throwable) {
-            LOG.error(e)
-        }
-        return emptyList()
-    }
-
-    override fun processTransferableData(
-        project: Project,
-        editor: Editor,
-        bounds: RangeMarker,
-        caretOffset: Int,
-        indented: Ref<Boolean>,
-        values: List<TextBlockTransferableData>
-    ) {
-        if (DumbService.getInstance(project).isDumb) return
-
-        val editorOptions = KtxEditorOptions.getInstance()
-        if (!editorOptions.enableXmlToKtxConversion) return
-
-        val targetFile = PsiDocumentManager.getInstance(project).getPsiFile(editor.document) as?
-                KtFile ?: return
-        val transferableData = values.single()
-        var (fileText, startOffsets, endOffsets) = when (transferableData) {
-            is CopiedXmlCode ->
-                Triple(
-                    transferableData.fileText,
-                    transferableData.startOffsets,
-                    transferableData.endOffsets
-                )
-            is CopiedText ->
-                Triple(
-                    transferableData.text,
-                    intArrayOf(0),
-                    intArrayOf(transferableData.text.length)
-                )
-            else ->
-                throw IllegalStateException("Unsupported transferable data: $transferableData")
-        }
-        var xmlFile = createXmlFile(project, fileText)
-
-        if (transferableData is CopiedText) {
-            if (hasErrors(xmlFile)) {
-                // Try to parse as attributes if we are inside a KTX tag.
-                val closestKtxTag =
-                    targetFile.findElementAt(caretOffset)?.parentOfType<KtxElement>() ?: return
-                val tagName =
-                    closestKtxTag.qualifiedTagName?.text ?: closestKtxTag.simpleTagName?.text
-                    ?: return
-
-                // Check that the caret is inside that tag attributes list.
-                val bracketsElements = closestKtxTag.bracketsElements
-                val firstLTPosition = bracketsElements
-                    .filter { it is TreeElement && it.elementType == KtTokens.LT }
-                    .map { it.startOffset }
-                    .min() ?: return
-                val firstGTPosition = bracketsElements
-                    .filter { it is TreeElement && it.elementType == KtTokens.GT }
-                    .map { it.startOffset }
-                    .min() ?: return
-
-                if (caretOffset < firstLTPosition || caretOffset > firstGTPosition) {
-                    return
-                }
-
-                // Create dummy XML tag and file.
-                val filePrefix = "<$tagName "
-                val newStartOffset = filePrefix.length
-                startOffsets = intArrayOf(newStartOffset)
-                endOffsets = intArrayOf(newStartOffset + fileText.length)
-                xmlFile = createXmlFile(project, "$filePrefix$fileText />")
-
-                if (hasErrors(xmlFile)) {
-                    return
-                }
-            }
-
-            // Make sure we don't try to convert copied KTX code.
-            if (!hasAttributeWithNamespace(xmlFile)) {
-                return
-            }
-        }
-
-        val ranges = startOffsets.mapIndexed { i, startOffset ->
-            TextRange(startOffset, endOffsets[i])
-        }
-        val elementAndTextList = ElementAndTextList().apply {
-            ranges.forEach { this.collectElementsToConvert(xmlFile, it) }
-        }
-
-        // Convert PsiElement's to AST Element's.
-        val conversionResult = StringBuilder()
-        val imports = hashSetOf<FqName>()
-        val converter = XmlToKtxConverter(targetFile)
-        var skipConversion = true
-        elementAndTextList.process(object : ElementsAndTextsProcessor {
-            override fun processElement(element: PsiElement) {
-                // TODO(jdemeulenaere): Better comment converter.
-                val codeBuilder = CodeBuilder(null, EmptyDocCommentConverter)
-                val resultKtx = converter.convertElement(element)
-                if (resultKtx != null) {
-                    skipConversion = false
-                    codeBuilder.append(resultKtx)
-                    imports.addAll(codeBuilder.importsToAdd)
-                    conversionResult.append(codeBuilder.resultText)
-                } else {
-                    conversionResult.append(element.text)
-                }
-            }
-
-            override fun processText(string: String) {
-                conversionResult.append(string)
-            }
-        })
-
-        if (skipConversion || !confirmConversion(project)) {
-            return
-        }
-
-        // Replace text.
-        val resultKtx = conversionResult.toString()
-        val enclosingCallable = targetFile.findElementAt(bounds.startOffset)?.let {
-            enclosingCallable(it)
-        }
-        val conversionResultText = if (enclosingCallable != null) {
-            resultKtx
-        } else {
-            // TODO(jdemeulenaere): Find better function name. If we are pasting in a class that has the same name as the file (common
-            // case), we will clash with the constructor. Maybe override invoke operator if it's not already overridden in that class or
-            // its subclasses ?
-            val functionName = targetFile.name.takeWhile { it != '.' }
-            createFunctionalComponent(
-                functionName,
-                resultKtx,
-                imports
-            )
-        }
-        val rangeAfterReplace = replaceText(editor, conversionResultText, bounds)
-        PsiDocumentManager.getInstance(project).commitAllDocuments()
-
-        // Suggest to add @Composable annotation and/or androidx.compose.* import if necessary.
-        if (editorOptions.enableAddComposableAnnotation) {
-            val fixes = arrayListOf<() -> Unit>()
-
-            // Add @Composable annotation.
-            if (enclosingCallable != null &&
-                // TODO(jdemeulenaere): For some reason, checking for annotations on lambdas doesn't work. Fix that and remove this check.
-                enclosingCallable is KtNamedFunction) {
-                val annotationFqName = ComposeUtils.composeFqName("Composable")
-                if (enclosingCallable.findAnnotation(annotationFqName) == null) {
-                    fixes.add {
-                        runWriteAction {
-                            enclosingCallable.addAnnotation(annotationFqName)
-                        }
-                    }
-                }
-            }
-
-            // Add androidx.compose.* import.
-            val composeStarFqName = ComposeUtils.composeFqName("*")
-            if (targetFile.importDirectives.none { it.importedFqName == composeStarFqName }) {
-                fixes.add { addComposeStarImport(targetFile) }
-            }
-
-            if (fixes.isNotEmpty()) {
-                // Ask for approval if necessary.
-                val shouldApplyFixes = if (editorOptions.donTShowAddComposableAnnotationDialog) {
-                    true
-                } else {
-                    val dialog =
-                        KtxAddComposableAnnotationDialog(
-                            project
-                        )
-                    dialog.show()
-                    dialog.isOK
-                }
-
-                // Apply fixes.
-                if (shouldApplyFixes) {
-                    fixes.forEach { it() }
-                }
-            }
-        }
-
-        // TODO(jdemeulenaere): If enclosingCallable == null (i.e. if we generated the enclosing function), put the cursor at the function
-        // name position and select it to easily change it right after pasting.
-
-        // Import classes.
-        if (imports.isNotEmpty()) {
-            runWriteAction {
-                imports.forEach { fqName ->
-                    targetFile.resolveImportReference(fqName).firstOrNull()?.let {
-                        ImportInsertHelper.getInstance(project).importDescriptor(targetFile, it)
-                    }
-                }
-            }
-
-            PsiDocumentManager.getInstance(project).commitAllDocuments()
-        }
-
-        // Format code.
-        XmlToKtxConverter.formatCode(
-            targetFile,
-            rangeAfterReplace
-        )
-        return
-    }
-
-    private fun createXmlFile(project: Project, fileText: String): XmlFile {
-        return PsiFileFactory.getInstance(project).createFileFromText(
-            XMLLanguage.INSTANCE, fileText
-        ) as XmlFile
-    }
-
-    private fun hasErrors(xmlFile: XmlFile) = xmlFile.anyDescendantOfType<PsiErrorElement>()
-
-    private fun enclosingCallable(element: PsiElement): KtCallableDeclaration? {
-        var current = element
-        while (current !is KtFile) {
-            if (current is KtCallableDeclaration) {
-                return current
-            }
-            current = current.parent
-        }
-        return null
-    }
-
-    private fun hasAttributeWithNamespace(file: XmlFile): Boolean {
-        val rootTag = file.rootTag ?: return false
-        return hasAttributeWithNamespace(rootTag)
-    }
-
-    private fun hasAttributeWithNamespace(tag: XmlTag): Boolean {
-        for (attribute in tag.attributes) {
-            if (attribute.namespacePrefix.isNotEmpty()) {
-                return true
-            }
-        }
-
-        for (subTag in tag.subTags) {
-            if (hasAttributeWithNamespace(subTag)) {
-                return true
-            }
-        }
-
-        return false
-    }
-
-    private fun replaceText(
-        editor: Editor,
-        conversionResultText: String,
-        bounds: RangeMarker
-    ): RangeMarker {
-        return runWriteAction {
-            val startOffset = bounds.startOffset
-            editor.document.replaceString(startOffset, bounds.endOffset, conversionResultText)
-
-            val endOffset = startOffset + conversionResultText.length
-            editor.caretModel.moveToOffset(endOffset)
-            editor.document.createRangeMarker(startOffset, endOffset).apply {
-                isGreedyToLeft = true
-                isGreedyToRight = true
-            }
-        }
-    }
-
-    private fun ElementAndTextList.collectElementsToConvert(file: PsiFile, range: TextRange) {
-        val elements = file.elementsInRange(range)
-        val fileText = file.text
-        if (elements.isEmpty()) {
-            add(fileText.substring(range.start, range.end))
-        } else {
-            add(fileText.substring(range.start, elements.first().range.start))
-            this += elements
-            add(fileText.substring(elements.last().range.end, range.end))
-        }
-    }
-
-    private fun confirmConversion(project: Project): Boolean {
-        if (KtxEditorOptions.getInstance().donTShowKtxConversionDialog) return true
-
-        val dialog = KtxPasteFromXmlDialog(project)
-        dialog.show()
-        return dialog.isOK
-    }
-}
\ No newline at end of file
diff --git a/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/conversion/EnumReflection.kt b/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/conversion/EnumReflection.kt
deleted file mode 100644
index 42357a7..0000000
--- a/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/conversion/EnumReflection.kt
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * Copyright 2018 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.compose.plugins.idea.conversion
-
-import com.intellij.codeInsight.AnnotationUtil
-import com.intellij.openapi.project.Project
-import com.intellij.psi.JavaPsiFacade
-import com.intellij.psi.PsiAnnotation
-import com.intellij.psi.PsiAnnotationMemberValue
-import com.intellij.psi.PsiArrayInitializerMemberValue
-import com.intellij.psi.PsiClass
-import com.intellij.psi.PsiModifierListOwner
-import com.intellij.psi.PsiReference
-import org.jetbrains.kotlin.asJava.elements.KtLightPsiLiteral
-import org.jetbrains.kotlin.descriptors.ClassDescriptor
-import org.jetbrains.kotlin.descriptors.ClassKind
-import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
-import org.jetbrains.kotlin.descriptors.FunctionDescriptor
-import org.jetbrains.kotlin.descriptors.PropertyDescriptor
-import org.jetbrains.kotlin.descriptors.ValueDescriptor
-import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
-import org.jetbrains.kotlin.descriptors.impl.EnumEntrySyntheticClassDescriptor
-import org.jetbrains.kotlin.idea.refactoring.fqName.getKotlinFqName
-import org.jetbrains.kotlin.idea.search.allScope
-import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
-import org.jetbrains.kotlin.name.FqName
-import androidx.compose.plugins.idea.AttributeInfo
-import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull
-import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyClassDescriptor
-import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered
-import org.jetbrains.kotlin.types.KotlinType
-import org.jetbrains.kotlin.types.typeUtil.isEnum
-import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
-import java.util.ArrayList
-
-private val MAGIC_CONSTANT_ANNOTATIONS = listOf("IntDef", "LongDef", "StringDef").flatMap {
-    listOf("android.annotation.$it", "android.support.annotation.$it")
-}
-
-fun AttributeInfo.getPossibleValues(project: Project): List<FqName> {
-    return getPossibleValues(
-        project,
-        descriptor ?: return emptyList()
-    )
-}
-
-internal fun getPossibleValues(project: Project, descriptor: DeclarationDescriptor): List<FqName> {
-    val valueDescriptor: ValueDescriptor = when (descriptor) {
-        is FunctionDescriptor -> descriptor.valueParameters.takeIf { it.size == 1 }?.first()
-        is PropertyDescriptor -> descriptor
-        is ValueParameterDescriptor -> descriptor
-        else -> null
-    } ?: return emptyList()
-
-    if (valueDescriptor.type.isEnum()) {
-        return getPossibleValuesFromEnum(valueDescriptor.type)
-    }
-
-    return getPossibleValuesFromAnnotations(
-        project,
-        valueDescriptor
-    )
-}
-
-internal fun getPossibleValuesFromEnum(type: KotlinType): List<FqName> {
-    return (type.constructor.declarationDescriptor as ClassDescriptor)
-        .unsubstitutedMemberScope
-        .getDescriptorsFiltered()
-        .filter {
-            it is EnumEntrySyntheticClassDescriptor ||
-                    (it is LazyClassDescriptor && it.kind == ClassKind.ENUM_ENTRY)
-        }
-        .mapNotNull { it.fqNameOrNull() }
-}
-
-private fun getPossibleValuesFromAnnotations(
-    project: Project,
-    descriptor: DeclarationDescriptor
-): List<FqName> {
-    val fqNames = arrayListOf<FqName>()
-
-    // When possible, use the overload taking PsiModifierListOwner as parameter as it has support for external annotations.
-    val psiElement = descriptor.findPsi() ?: descriptor.original.findPsi()
-    if (psiElement != null && psiElement is PsiModifierListOwner) {
-        fillPossibleEnumValues(
-            project,
-            psiElement,
-            fqNames,
-            hashSetOf()
-        )
-    } else {
-        val visited = hashSetOf<PsiClass>()
-        descriptor.annotations.forEach {
-            val annotationFqName = it.fqName?.asString() ?: return@forEach
-            fillPossibleEnumValues(
-                project,
-                annotationFqName,
-                fqNames,
-                visited
-            )
-        }
-    }
-
-    return fqNames
-}
-
-private fun fillPossibleEnumValues(
-    project: Project,
-    annotationFqName: String,
-    enumFqNames: ArrayList<FqName>,
-    visited: MutableSet<PsiClass>
-) {
-    val annotationClass = JavaPsiFacade.getInstance(project).findClass(
-        annotationFqName,
-        project.allScope()
-    ) ?: return
-    if (!visited.add(annotationClass)) return
-
-    // We use the navigationElement if possible such that we also get annotations with SOURCE retention.
-    val annotated = (annotationClass.navigationElement as? PsiModifierListOwner) ?: annotationClass
-    fillPossibleEnumValues(
-        project,
-        annotated,
-        enumFqNames,
-        visited
-    )
-}
-
-private fun fillPossibleEnumValues(
-    project: Project,
-    annotated: PsiModifierListOwner,
-    enumFqNames: ArrayList<FqName>,
-    visited: MutableSet<PsiClass>
-) {
-    val annotations = AnnotationUtil.getAllAnnotations(
-        annotated,
-        /* inHierarchy= */ true,
-        /* visited= */ null
-    )
-    for (annotation in annotations) {
-        val qualifiedName = annotation.qualifiedName ?: continue
-        if (qualifiedName in MAGIC_CONSTANT_ANNOTATIONS) {
-            enumFqNames.addAll(
-                getMagicConstantEnumValues(
-                    annotation
-                )
-            )
-        } else {
-            fillPossibleEnumValues(
-                project,
-                qualifiedName,
-                enumFqNames,
-                visited
-            )
-        }
-    }
-}
-
-private fun getMagicConstantEnumValues(annotation: PsiAnnotation): List<FqName> {
-    val valueAttribute = annotation.findAttributeValue("value")
-    val allowedValues = (valueAttribute as? PsiArrayInitializerMemberValue)?.initializers
-        ?: PsiAnnotationMemberValue.EMPTY_ARRAY
-    return allowedValues
-        .map<Any, Array<out PsiReference>> {
-            when (it) {
-                is PsiReference -> arrayOf(it)
-                // TODO(jdemeulenaere): Fix the case when the value is expressed as a KtDotQualifiedExpression (i.e. 'View.VISIBLE' instead
-                // of 'VISIBLE').
-                is KtLightPsiLiteral -> it.references
-                else -> emptyArray()
-            }
-        }
-        .mapNotNull {
-                references -> references.firstNotNullResult { it.resolve()?.getKotlinFqName() }
-        }
-}
\ No newline at end of file
diff --git a/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/conversion/KtxAddComposableAnnotationDialog.form b/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/conversion/KtxAddComposableAnnotationDialog.form
deleted file mode 100644
index 52a86f4..0000000
--- a/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/conversion/KtxAddComposableAnnotationDialog.form
+++ /dev/null
@@ -1,34 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="androidx.compose.plugins.idea.conversion.KtxAddComposableAnnotationDialog">
-  <grid id="27dc6" binding="panel" layout-manager="GridLayoutManager" row-count="3" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
-    <margin top="0" left="0" bottom="0" right="0"/>
-    <constraints>
-      <xy x="20" y="20" width="640" height="400"/>
-    </constraints>
-    <properties/>
-    <border type="none"/>
-    <children>
-      <component id="746fc" class="javax.swing.JLabel">
-        <constraints>
-          <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
-        </constraints>
-        <properties>
-          <text value="Looks like your code is missing the @Composable annotation and/or the androidx.compose.* import. Do you want to fix that?"/>
-        </properties>
-      </component>
-      <vspacer id="b8512">
-        <constraints>
-          <grid row="2" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
-        </constraints>
-      </vspacer>
-      <component id="98483" class="javax.swing.JCheckBox" binding="donTShowThisCheckBox" default-binding="true">
-        <constraints>
-          <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
-        </constraints>
-        <properties>
-          <text value="&amp;Don't show this dialog next time"/>
-        </properties>
-      </component>
-    </children>
-  </grid>
-</form>
\ No newline at end of file
diff --git a/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/conversion/KtxAddComposableAnnotationDialog.kt b/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/conversion/KtxAddComposableAnnotationDialog.kt
deleted file mode 100644
index d12df35..0000000
--- a/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/conversion/KtxAddComposableAnnotationDialog.kt
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright 2018 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.compose.plugins.idea.conversion
-
-import com.intellij.CommonBundle
-import com.intellij.openapi.project.Project
-import com.intellij.openapi.ui.DialogWrapper
-import androidx.compose.plugins.idea.editor.KtxEditorOptions
-import java.awt.Container
-import javax.swing.Action
-import javax.swing.JCheckBox
-import javax.swing.JComponent
-import javax.swing.JPanel
-
-class KtxAddComposableAnnotationDialog(
-    project: Project
-) : DialogWrapper(project, false) {
-    private lateinit var panel: JPanel
-    private lateinit var donTShowThisCheckBox: JCheckBox
-
-    init {
-        isModal = true
-        title = "Add @Composable annotation"
-        init()
-    }
-
-    override fun createCenterPanel(): JComponent = panel
-
-    override fun getContentPane(): Container = panel
-
-    override fun createActions(): Array<Action> {
-        setOKButtonText(CommonBundle.getYesButtonText())
-        setCancelButtonText(CommonBundle.getNoButtonText())
-        return arrayOf(okAction, cancelAction)
-    }
-
-    override fun doOKAction() {
-        if (donTShowThisCheckBox.isSelected) {
-            KtxEditorOptions.getInstance().donTShowAddComposableAnnotationDialog = true
-        }
-        super.doOKAction()
-    }
-
-    override fun doCancelAction() {
-        if (donTShowThisCheckBox.isSelected) {
-            KtxEditorOptions.getInstance().enableAddComposableAnnotation = false
-            KtxEditorOptions.getInstance().donTShowAddComposableAnnotationDialog = true
-        }
-        super.doCancelAction()
-    }
-}
\ No newline at end of file
diff --git a/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/conversion/KtxPasteFromXmlDialog.form b/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/conversion/KtxPasteFromXmlDialog.form
deleted file mode 100644
index 8a4f6d3..0000000
--- a/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/conversion/KtxPasteFromXmlDialog.form
+++ /dev/null
@@ -1,34 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="androidx.compose.plugins.idea.conversion.KtxPasteFromXmlDialog">
-  <grid id="27dc6" binding="panel" layout-manager="GridLayoutManager" row-count="3" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
-    <margin top="0" left="0" bottom="0" right="0"/>
-    <constraints>
-      <xy x="20" y="20" width="515" height="400"/>
-    </constraints>
-    <properties/>
-    <border type="none"/>
-    <children>
-      <component id="746fc" class="javax.swing.JLabel">
-        <constraints>
-          <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
-        </constraints>
-        <properties>
-          <text value="Clipboard content copied from XML file. Do you want to convert it to Kotlin KTX code?"/>
-        </properties>
-      </component>
-      <vspacer id="b8512">
-        <constraints>
-          <grid row="2" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
-        </constraints>
-      </vspacer>
-      <component id="98483" class="javax.swing.JCheckBox" binding="donTShowThisCheckBox" default-binding="true">
-        <constraints>
-          <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
-        </constraints>
-        <properties>
-          <text value="&amp;Don't show this dialog next time"/>
-        </properties>
-      </component>
-    </children>
-  </grid>
-</form>
diff --git a/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/conversion/KtxPasteFromXmlDialog.kt b/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/conversion/KtxPasteFromXmlDialog.kt
deleted file mode 100644
index 573e5a8..0000000
--- a/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/conversion/KtxPasteFromXmlDialog.kt
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright 2018 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.compose.plugins.idea.conversion
-
-import com.intellij.CommonBundle
-import com.intellij.openapi.project.Project
-import com.intellij.openapi.ui.DialogWrapper
-import androidx.compose.plugins.idea.editor.KtxEditorOptions
-import java.awt.Container
-import javax.swing.Action
-import javax.swing.JCheckBox
-import javax.swing.JComponent
-import javax.swing.JPanel
-
-class KtxPasteFromXmlDialog(project: Project) : DialogWrapper(project, false) {
-    private lateinit var panel: JPanel
-    private lateinit var donTShowThisCheckBox: JCheckBox
-
-    init {
-        isModal = true
-        title = "Convert Code From XML"
-        init()
-    }
-
-    override fun createCenterPanel(): JComponent = panel
-
-    override fun getContentPane(): Container = panel
-
-    override fun createActions(): Array<Action> {
-        setOKButtonText(CommonBundle.getYesButtonText())
-        setCancelButtonText(CommonBundle.getNoButtonText())
-        return arrayOf(okAction, cancelAction)
-    }
-
-    override fun doOKAction() {
-        if (donTShowThisCheckBox.isSelected) {
-            KtxEditorOptions.getInstance().donTShowKtxConversionDialog = true
-        }
-        super.doOKAction()
-    }
-}
\ No newline at end of file
diff --git a/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/conversion/XmlToComponentAction.kt b/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/conversion/XmlToComponentAction.kt
deleted file mode 100644
index 7bb065e..0000000
--- a/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/conversion/XmlToComponentAction.kt
+++ /dev/null
@@ -1,236 +0,0 @@
-/*
- * Copyright 2018 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.compose.plugins.idea.conversion
-
-import com.google.common.base.CaseFormat
-import com.intellij.openapi.actionSystem.AnAction
-import com.intellij.openapi.actionSystem.AnActionEvent
-import com.intellij.openapi.actionSystem.CommonDataKeys
-import com.intellij.openapi.fileEditor.FileDocumentManager
-import com.intellij.openapi.fileEditor.FileEditorManager
-import com.intellij.openapi.project.Project
-import com.intellij.openapi.vfs.VfsUtilCore
-import com.intellij.openapi.vfs.VirtualFile
-import com.intellij.openapi.vfs.VirtualFileVisitor
-import com.intellij.psi.PsiDocumentManager
-import com.intellij.psi.PsiManager
-import com.intellij.psi.xml.XmlFile
-import org.jetbrains.kotlin.idea.caches.resolve.resolveImportReference
-import org.jetbrains.kotlin.idea.j2k.EmptyDocCommentConverter
-import org.jetbrains.kotlin.idea.core.util.toPsiFile
-import org.jetbrains.kotlin.idea.util.ImportInsertHelper
-import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
-import org.jetbrains.kotlin.idea.util.application.runWriteAction
-import org.jetbrains.kotlin.j2k.CodeBuilder
-import org.jetbrains.kotlin.name.FqName
-import org.jetbrains.kotlin.psi.KtFile
-import java.io.File
-
-class XmlToComponentAction : AnAction() {
-    companion object {
-        fun convertFiles(xmlFiles: List<XmlFile>) {
-            if (xmlFiles.isEmpty()) {
-                return
-            }
-
-            // TODO(jdemeulenaere): Check if there are syntax errors in any file, in which case either abort or warn that result might be incorrect.
-
-            val project = xmlFiles.first().project
-            project.executeWriteCommand("Convert XML Files to Component") {
-                val convertedFiles = xmlFiles.map {
-                    convertFile(
-                        it
-                    )
-                }
-                PsiDocumentManager.getInstance(project).commitAllDocuments()
-
-                // Open first converted file.
-                FileEditorManager.getInstance(project).openFile(
-                    convertedFiles.first().virtualFile,
-                    true
-                )
-            }
-        }
-
-        private fun convertFile(sourceFile: XmlFile): KtFile {
-            val copy = sourceFile.copy() as XmlFile
-            val targetFile =
-                renameFile(
-                    sourceFile
-                )
-
-            val resultKtx = XmlToKtxConverter(targetFile).convertElement(copy)
-            val (content, imports) = if (resultKtx != null) {
-                // TODO(jdemeulenaere): Better comment converter.
-                val codeBuilder = CodeBuilder(null, EmptyDocCommentConverter)
-                codeBuilder.append(resultKtx)
-                val functionName =
-                    functionName(
-                        copy
-                    )
-                val imports = codeBuilder.importsToAdd.toMutableSet()
-                val content = createFunctionalComponent(
-                    functionName,
-                    codeBuilder.resultText,
-                    imports
-                )
-                Pair(content, imports)
-            } else {
-                Pair(copy.text, emptySet<FqName>())
-            }
-
-            replaceContent(
-                targetFile,
-                content
-            )
-            addImports(
-                targetFile,
-                imports
-            )
-            XmlToKtxConverter.formatCode(
-                targetFile
-            )
-            return targetFile
-        }
-
-        private fun functionName(file: XmlFile): String {
-            // TODO(jdemeulenaere): Ask the user which file/function name to use (and pre-fill name with this algorithm) if converting only one file.
-            var functionName = file.name
-
-            // Remove extension.
-            val dotIndex = functionName.lastIndexOf('.')
-            if (dotIndex != -1) {
-                functionName = functionName.substring(0, dotIndex)
-            }
-
-            // Remove special characters.
-            functionName = functionName.replace(Regex("[^A-Za-z0-9_]"), "")
-
-            // Strip activity_* prefix away.
-            if (functionName.startsWith("activity_")) {
-                functionName = functionName.substring("activity_".length)
-            }
-
-            // Convert case.
-            return CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, functionName)
-        }
-
-        private fun replaceContent(file: KtFile, content: String) {
-            // Replace content.
-            val documentManager = PsiDocumentManager.getInstance(file.project)
-            val document = documentManager.getDocument(file)!!
-            document.replaceString(0, document.textLength, content)
-            FileDocumentManager.getInstance().saveDocument(document)
-            documentManager.commitDocument(document)
-        }
-
-        private fun renameFile(sourceFile: XmlFile): KtFile {
-            // TODO(jdemeulenaere): If we are converting only one file, ask the user in which folder we should move that file.
-            // TODO(jdemeulenaere): Handle scratch files (change language mapping).
-            val virtualFile = sourceFile.virtualFile
-            val ioFile = File(virtualFile.path.replace('/', File.separatorChar))
-            val className =
-                functionName(
-                    sourceFile
-                )
-            var kotlinFileName = "$className.kt"
-            var i = 1
-            while (true) {
-                if (!ioFile.resolveSibling(kotlinFileName).exists()) break
-                kotlinFileName = "$className${i++}.kt"
-            }
-            virtualFile.rename(this, kotlinFileName)
-            return virtualFile.toPsiFile(sourceFile.project)!! as KtFile
-        }
-
-        private fun addImports(ktFile: KtFile, imports: Collection<FqName>) {
-            runWriteAction {
-                imports.forEach { fqName ->
-                    ktFile.resolveImportReference(fqName).firstOrNull()?.let {
-                        ImportInsertHelper.getInstance(ktFile.project).importDescriptor(ktFile, it)
-                    }
-                }
-            }
-            addComposeStarImport(ktFile)
-        }
-
-        private fun isAnyXmlFileSelected(project: Project, files: Array<VirtualFile>): Boolean {
-            val manager = PsiManager.getInstance(project)
-
-            if (files.any { manager.findFile(it) is XmlFile && it.isWritable }) return true
-            return files.any { it.isDirectory && isAnyXmlFileSelected(
-                project,
-                it.children
-            )
-            }
-        }
-
-        private fun selectedXmlFiles(event: AnActionEvent): Sequence<XmlFile> {
-            val virtualFiles = event.getData(CommonDataKeys.VIRTUAL_FILE_ARRAY)
-                ?: return sequenceOf()
-            val project = event.project ?: return sequenceOf()
-            return allXmlFiles(
-                virtualFiles,
-                project
-            )
-        }
-
-        private fun allXmlFiles(
-            filesOrDirs: Array<VirtualFile>,
-            project: Project
-        ): Sequence<XmlFile> {
-            val manager = PsiManager.getInstance(project)
-            return allFiles(
-                filesOrDirs
-            )
-                .asSequence()
-                .mapNotNull { manager.findFile(it) as? XmlFile }
-        }
-
-        private fun allFiles(filesOrDirs: Array<VirtualFile>): Collection<VirtualFile> {
-            val result = ArrayList<VirtualFile>()
-            for (file in filesOrDirs) {
-                VfsUtilCore.visitChildrenRecursively(file, object : VirtualFileVisitor<Unit>() {
-                    override fun visitFile(file: VirtualFile): Boolean {
-                        result.add(file)
-                        return true
-                    }
-                })
-            }
-            return result
-        }
-    }
-
-    override fun actionPerformed(e: AnActionEvent) {
-        val xmlFiles = selectedXmlFiles(
-            e
-        ).filter { it.isWritable }.toList()
-        convertFiles(
-            xmlFiles
-        )
-    }
-
-    override fun update(e: AnActionEvent) {
-        val virtualFiles = e.getData(CommonDataKeys.VIRTUAL_FILE_ARRAY) ?: return
-        val project = e.project ?: return
-        e.presentation.isVisible =
-            isAnyXmlFileSelected(
-                project,
-                virtualFiles
-            )
-    }
-}
\ No newline at end of file
diff --git a/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/conversion/XmlToKtxConverter.kt b/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/conversion/XmlToKtxConverter.kt
deleted file mode 100644
index 490dfec..0000000
--- a/compose/compose-ide-plugin/src/main/java/androidx/compose/plugins/idea/conversion/XmlToKtxConverter.kt
+++ /dev/null
@@ -1,381 +0,0 @@
-/*
- * Copyright 2018 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.compose.plugins.idea.conversion
-
-import com.intellij.openapi.editor.RangeMarker
-import com.intellij.psi.JavaPsiFacade
-import com.intellij.psi.PsiClass
-import com.intellij.psi.PsiElement
-import com.intellij.psi.PsiFile
-import com.intellij.psi.codeStyle.CodeStyleManager
-import com.intellij.psi.xml.XmlAttribute
-import com.intellij.psi.xml.XmlFile
-import com.intellij.psi.xml.XmlTag
-import org.jetbrains.kotlin.builtins.DefaultBuiltIns
-import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
-import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithVisibility
-import org.jetbrains.kotlin.descriptors.resolveClassByFqName
-import org.jetbrains.kotlin.idea.caches.resolve.findModuleDescriptor
-import org.jetbrains.kotlin.idea.caches.resolve.util.getJavaClassDescriptor
-import org.jetbrains.kotlin.idea.core.isExcludedFromAutoImport
-import org.jetbrains.kotlin.idea.core.isVisible
-import org.jetbrains.kotlin.idea.imports.importableFqName
-import org.jetbrains.kotlin.idea.search.allScope
-import org.jetbrains.kotlin.idea.util.application.runWriteAction
-import org.jetbrains.kotlin.incremental.components.NoLookupLocation
-import org.jetbrains.kotlin.j2k.ast.DeclarationStatement
-import org.jetbrains.kotlin.j2k.ast.Element
-import org.jetbrains.kotlin.j2k.ast.Expression
-import org.jetbrains.kotlin.j2k.ast.KtxAttribute
-import org.jetbrains.kotlin.j2k.ast.KtxElement
-import org.jetbrains.kotlin.j2k.ast.LiteralExpression
-import org.jetbrains.kotlin.name.FqName
-import org.jetbrains.kotlin.psi.KtFile
-import androidx.compose.plugins.idea.AttributeInfo
-import androidx.compose.plugins.idea.AttributeInfoExtractor
-import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull
-import org.jetbrains.kotlin.resolve.descriptorUtil.getAllSuperClassifiers
-import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
-import org.jetbrains.kotlin.types.KotlinType
-import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
-import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
-
-class XmlToKtxConverter(private val targetFile: KtFile) {
-    companion object {
-        private const val NAMESPACE_PREFIX = "xmlns"
-        private val NON_ALPHANUMERIC_PATTERN = Regex("[^A-Za-z0-9]")
-
-        private val CLASS_PREFIX_LIST = listOf(
-            "android.widget.",
-            "android.webkit.",
-            "android.app.",
-            "android.view."
-        )
-
-        private enum class SpecialTags(val tagName: String) {
-            VIEW("view"),
-            BLINK("blink"),
-            MERGE("merge"),
-            INCLUDE("include"),
-            REQUEST_FOCUS("requestFocus"),
-            TAG("tag")
-        }
-
-        enum class XmlNamespace(val uri: String, val defaultPrefix: String) {
-            ANDROID("http://schemas.android.com/apk/res/android", "android"),
-            ANDROID_TOOLS("http://schemas.android.com/tools", "tools"),
-            ANDROID_APP("http://schemas.android.com/apk/res-auto", "app");
-        }
-
-        fun formatCode(file: PsiFile, range: RangeMarker? = null) {
-            runWriteAction {
-                val codeStyleManager = CodeStyleManager.getInstance(file.project)
-                if (range != null && range.isValid) {
-                    codeStyleManager.reformatRange(file, range.startOffset, range.endOffset)
-                } else {
-                    codeStyleManager.reformat(file)
-                }
-            }
-        }
-    }
-
-    private val project = targetFile.project
-    // TODO(jdemeulenaere): Compare with `isVisibleDescriptor` in (BaseCompose)CompletionSession to add more checks here.
-    private val visibilityFilter: (DeclarationDescriptor) -> Boolean = lambda@{ descriptor ->
-        if (descriptor is DeclarationDescriptorWithVisibility) {
-            return@lambda descriptor.isVisible(targetFile.findModuleDescriptor())
-        }
-
-        return@lambda descriptor.isExcludedFromAutoImport(project, targetFile)
-    }
-    private val attributeInfoExtractor =
-        AttributeInfoExtractor(targetFile, visibilityFilter)
-
-    fun convertElement(element: PsiElement): Element? = when (element) {
-        is XmlFile -> convertFile(element)
-        is XmlTag -> convertTag(element)
-        // We return an empty element as `maybeConvertAttribute` returns null if the attribute should be skipped.
-        is XmlAttribute -> maybeConvertAttribute(element) ?: Element.Empty
-        else -> null
-    }
-
-    private fun convertFile(element: XmlFile): Element {
-        return element.rootTag?.let { convertTag(it) } ?: Element.Empty
-    }
-
-    private val valueConversionsCache = hashMapOf<Pair<String, String>, List<ValueConversion>>()
-    private fun getValueConversions(
-        tagQualifiedName: String,
-        xmlAttributeName: String
-    ): List<ValueConversion> {
-        return valueConversionsCache.computeIfAbsent(Pair(tagQualifiedName, xmlAttributeName)) {
-            val descriptor = findClass(tagQualifiedName)?.getJavaClassDescriptor()
-                ?: return@computeIfAbsent emptyList()
-            val classNames = descriptor.getAllSuperClassifiers().toList().mapNotNull {
-                it.fqNameOrNull()?.asString()
-            }.toSet()
-            ANDROID_CONVERSION.classConversions
-                .filter { it.matchesAny(classNames) }
-                .flatMap { it.attributeConversions }
-                .filter { it.matches(xmlAttributeName) }
-                .flatMap { it.valueConversions }
-        }
-    }
-
-    private val attributeInfosCache = hashMapOf<String, Map<String, List<AttributeInfo>>>()
-    private fun getAttributeInfos(
-        tagQualifiedName: String,
-        ktxAttributeName: String
-    ): List<AttributeInfo> {
-        return attributeInfosCache.computeIfAbsent(tagQualifiedName) {
-            val descriptor = findClass(tagQualifiedName)?.getJavaClassDescriptor()
-                ?: return@computeIfAbsent emptyMap()
-            val attributeInfos = arrayListOf<AttributeInfo>()
-            // TODO(jdemeulenaere): Allow to filter with ktxAttributeName at the AttributeInfoExtractor level instead.
-            attributeInfoExtractor.extract(descriptor) { attributeInfos.addAll(it) }
-            attributeInfos.groupBy { it.name }
-        }[ktxAttributeName] ?: emptyList()
-    }
-
-    private fun findClass(qualifiedName: String): PsiClass? {
-        return JavaPsiFacade.getInstance(project).findClass(qualifiedName, project.allScope())
-    }
-
-    private data class EnumValue(
-        val attributeInfo: AttributeInfo,
-        val fqNames: List<FqName>
-    )
-
-    private val enumValuesCache = hashMapOf<Pair<String, String>, List<EnumValue>>()
-    private fun getEnumValues(tagQualifiedName: String, ktxAttributeName: String): List<EnumValue> {
-        // TODO(jdemeulenaere): Precompute those for Android views or download android SDK & external annotations jars.
-        // TODO(jdemeulenaere): We might want to somehow filter those, as some incorrect attribute conversions will be returned (e.g.
-        // "notFocusable" in XML will be mapped to android.view.View.NOT_FOCUSABLE, which does not exist).
-        return enumValuesCache.computeIfAbsent(Pair(tagQualifiedName, ktxAttributeName)) {
-            getAttributeInfos(tagQualifiedName, ktxAttributeName).mapNotNull { attributeInfo ->
-                attributeInfo.getPossibleValues(project)
-                    .takeIf { it.isNotEmpty() }
-                    ?.let {
-                        EnumValue(
-                            attributeInfo,
-                            it
-                        )
-                    }
-            }
-        }
-    }
-
-    /**
-     * Returns the common prefix length of the possible values, e.g: :
-     *   getCommonPrefixLength(["TEXT_ALIGNMENT_TEXT_START", "TEXT_ALIGNMENT_CENTER"]) == "TEXT_ALIGNMENT_".length
-     */
-    private fun getCommonPrefixLength(possibleValues: List<String>): Int {
-        // TODO(jdemeulenaere): In the latest android sources, the @IntDef attribute has a prefix value that gives exactly the prefix we
-        // want to remove. Use that when generating the conversion configuration.
-
-        var lastUnderscoreIndex = -1
-        val minLength = possibleValues.map(String::length).min()!!
-        for (i in 0 until minLength) {
-            val char = possibleValues[0][i]
-            if (!possibleValues.all { it[i] == char }) {
-                break
-            }
-            if (char == '_') {
-                lastUnderscoreIndex = i
-            }
-        }
-        return lastUnderscoreIndex + 1
-    }
-
-    private fun convertTag(tag: XmlTag): Element {
-        when (tag.name) {
-            Companion.SpecialTags.MERGE.tagName -> return DeclarationStatement(tag.subTags.map {
-                convertTag(it)
-            })
-            // TODO(jdemeulenaere): Instead of ignoring the <include> tag, we might want to suggest the user to convert the included layout
-            // file into a component, or inline and convert its content.
-            Companion.SpecialTags.INCLUDE.tagName,
-            // TODO(jdemeulenaere): Make sure we want to ignore <tag> and <requestFocus/> tags.
-            Companion.SpecialTags.TAG.tagName,
-            Companion.SpecialTags.REQUEST_FOCUS.tagName -> return Element.Empty
-        }
-
-        val attributes = tag.attributes.mapNotNull(::maybeConvertAttribute)
-        val body = tag.subTags.map(::convertTag)
-        val (simpleName, qualifiedName) = getTagSimpleAndQualifiedName(tag)
-        return KtxElement(simpleName.asIdentifier(qualifiedName), attributes, body)
-    }
-
-    private fun getTagSimpleAndQualifiedName(tag: XmlTag): Pair<String, String> {
-        val fullName = when (tag.name) {
-            Companion.SpecialTags.VIEW.tagName -> tag.getAttributeValue("class")
-                ?: "android.view.View"
-            // The <blink></blink> tag will be inflated as a android.view.LayoutInflater.BlinkLayout, which is a private class that extends
-            // FrameLayout. User will lose the blinking but this is the closest we can get.
-            Companion.SpecialTags.BLINK.tagName -> "android.widget.FrameLayout"
-            else -> tag.name
-        }
-
-        val dotIndex = fullName.lastIndexOf('.')
-        if (dotIndex != -1) {
-            // TODO(jdemeulenaere): Check that import doesn't conflict with another (existing or future) import (in which case use the fully qualified name).
-            return fullName.substring(dotIndex + 1) to fullName
-        }
-
-        val qualifiedName = CLASS_PREFIX_LIST
-            .map { "$it$fullName" }
-            .firstOrNull {
-                JavaPsiFacade.getInstance(project).findClass(it, project.allScope()) != null
-            } ?: "android.widget.$fullName"
-        return fullName to qualifiedName
-    }
-
-    private fun XmlAttribute.isInNamespace(ns: XmlNamespace): Boolean {
-        return if (namespace.isNotEmpty()) {
-            namespace == ns.uri
-        } else {
-            namespacePrefix == ns.defaultPrefix
-        }
-    }
-
-    private fun shouldConvertAttribute(attribute: XmlAttribute): Boolean {
-        return attribute.namespacePrefix != NAMESPACE_PREFIX &&
-                !attribute.isInNamespace(Companion.XmlNamespace.ANDROID_TOOLS) &&
-                !(attribute.value ?: "").startsWith("@+id/") &&
-                (attribute.parent.name != Companion.SpecialTags.VIEW.tagName ||
-                        attribute.name != "class")
-    }
-
-    private fun maybeConvertAttribute(attribute: XmlAttribute): KtxAttribute? {
-        if (!shouldConvertAttribute(attribute)) {
-            return null
-        }
-
-        return convertAttribute(attribute)
-    }
-
-    private fun convertAttribute(attribute: XmlAttribute): KtxAttribute {
-        val xmlName = attribute.localName // Strips away the namespace.
-        val ktxName = getKtxAttributeName(xmlName)
-        val xmlValue = attribute.value ?: ""
-
-        val tag: XmlTag? = attribute.parent
-
-        // Convert the value.
-        val tagQualifiedName = tag?.let { getTagSimpleAndQualifiedName(it).second }
-        val valueConversions = tagQualifiedName?.let {
-            getValueConversions(it, xmlName)
-        } ?: emptyList()
-        val conversionResult = valueConversions
-            .asSequence()
-            .filter { it.matches(xmlValue) }
-            .mapNotNull {
-                it.convert(object : ConversionContext {
-                    override val xmlName = xmlName
-                    override val xmlValue = xmlValue
-                })
-            }
-            .firstIsInstanceOrNull<ConversionResult.Success>()
-
-        var valueType: KotlinType? = null
-        var valueExpression: Expression? = null
-        if (conversionResult != null) {
-            // If conversion from the ConversionConfiguration succeeded, we end up with an Expression and the fully qualified name of this
-            // expression type. We compare the fully qualified name with the KotlinType of the AttributeInfos corresponding to this attribute
-            // to try to guess if we should import any extension function/property.
-            // The current logic is that if there is one and only one AttributeInfo with that type, and that AttributeInfo is associated to
-            // an extension, then we import it, otherwise we don't.
-
-            // TODO(jdemeulenaere): Make sure that the comparison with f.q. name from converter and KotlinType from AttributeInfo works well
-            // for all cases (e.g. generics).
-
-            valueExpression = conversionResult.expression
-
-            // TODO(jdemeulenaere/lelandr): There is certainly a better way to map FqName => KotlinType.
-            val fqName = conversionResult.kotlinType
-            val classDescriptor = DefaultBuiltIns.Instance.builtInsModule.resolveClassByFqName(
-                fqName,
-                NoLookupLocation.FROM_BUILTINS
-            ) ?: findClass(fqName.asString())?.getJavaClassDescriptor()
-            valueType = classDescriptor?.defaultType
-        } else {
-            // If conversion from ConversionConfiguration failed, then we try to match the XML value to an enum using reflection.
-
-            val enumValues = tagQualifiedName?.let { getEnumValues(it, ktxName) }
-            enumValues?.forEach { (attributeInfo, possibleValues) ->
-                assert(possibleValues.isNotEmpty())
-
-                // TODO(jdemeulenaere): We certainly want to remove common prefix only for values coming from magic annotations and not
-                //  actual enum classes.
-                val commonPrefixLength =
-                    if (possibleValues.size == 1) 0 else getCommonPrefixLength(possibleValues.map {
-                        it.shortName().asString()
-                    })
-
-                // We remove all non alpha numeric characters and lower case when matching values.
-                fun simplify(s: String) = s.replace(NON_ALPHANUMERIC_PATTERN, "").toLowerCase()
-
-                possibleValues.forEach { fqName ->
-                    val shortName = fqName.shortName().asString()
-                    val upperUnderscoreValue = shortName.substring(commonPrefixLength)
-
-                    if (simplify(upperUnderscoreValue) == simplify(xmlValue)) {
-                        val nameImport = attributeInfo.descriptor?.takeIf {
-                            it.isExtension
-                        }?.importableFqName
-                        return KtxAttribute(
-                            ktxName.asIdentifier(nameImport),
-                            shortName.asIdentifier(fqName)
-                        )
-                    }
-                }
-            }
-        }
-
-        if (valueExpression == null) {
-            valueExpression = LiteralExpression(xmlValue.quoted())
-            valueType = DefaultBuiltIns.Instance.stringType
-        }
-
-        // Add import if we are using an extension function.
-        val attributeImport = valueType?.let { theValueType ->
-            val attributeInfos = tagQualifiedName?.let { getAttributeInfos(it, ktxName) }
-                ?: emptyList()
-            attributeInfos
-                .filter { theValueType.isSubtypeOf(it.type) }
-                // Only import extension function if it's the only one matching that KotlinType.
-                .takeIf { it.size == 1 }
-                ?.firstOrNull { it.isExtension }
-                ?.let { it.descriptor?.importableFqName }
-        }
-
-        return KtxAttribute(ktxName.asIdentifier(attributeImport), valueExpression)
-    }
-
-    private fun getKtxAttributeName(
-        xmlName: String
-    ) = xmlName.replace(Regex("_([a-zA-Z\\d])")) { it.groupValues[1].toUpperCase() }
-
-    private fun String.quoted() = "\"${escaped()}\""
-
-    private fun String.escaped(): String {
-        return this
-            .replace("\\", "\\\\") // Replaces all \ by \\
-            .replace("\"", "\\\"") // Replaces all " by \"
-            .replace("\$", "\\\$") // Replaces all $ by \$
-    }
-}
\ No newline at end of file
diff --git a/compose/compose-ide-plugin/src/main/resources/META-INF/plugin.xml b/compose/compose-ide-plugin/src/main/resources/META-INF/plugin.xml
index a85a310..3e5bfc9 100644
--- a/compose/compose-ide-plugin/src/main/resources/META-INF/plugin.xml
+++ b/compose/compose-ide-plugin/src/main/resources/META-INF/plugin.xml
@@ -37,7 +37,7 @@
     <extensions defaultExtensionNs="org.jetbrains.kotlin">
         <ktxParsingExtension implementation="androidx.compose.plugins.kotlin.ComposeKtxParsingExtension" />
         <storageComponentContainerContributor implementation="androidx.compose.plugins.kotlin.ComponentsClosedDeclarationChecker" />
-        <storageComponentContainerContributor implementation="androidx.compose.plugins.idea.ComposableAnnotationChecker" />
+        <storageComponentContainerContributor implementation="androidx.compose.plugins.kotlin.ComposableAnnotationChecker" />
         <storageComponentContainerContributor implementation="androidx.compose.plugins.kotlin.UnionAnnotationCheckerProvider" />
         <ktxTypeResolutionExtension implementation="androidx.compose.plugins.kotlin.ComposeKtxTypeResolutionExtension"/>
         <callResolutionInterceptorExtension implementation="androidx.compose.plugins.kotlin.ComposeCallResolutionInterceptorExtension"/>
@@ -47,12 +47,6 @@
         <diagnosticSuppressor implementation="androidx.compose.plugins.kotlin.ComposeDiagnosticSuppressor"/>
         <quickFixContributor implementation="androidx.compose.plugins.idea.quickfix.ComposeQuickFixRegistry" />
     </extensions>
-    <actions>
-        <action id="ConvertXmlToComponent" class="androidx.compose.plugins.idea.conversion.XmlToComponentAction"
-            text="Convert XML File to Component">
-            <add-to-group group-id="ConvertJavaToKotlinGroup" anchor="last"/>
-        </action>
-    </actions>
 
     <application-components>
         <component>
diff --git a/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/dbmonster/DbMonster.kt b/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/dbmonster/DbMonster.kt
index da720b8..6e183dd 100644
--- a/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/dbmonster/DbMonster.kt
+++ b/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/dbmonster/DbMonster.kt
@@ -22,6 +22,7 @@
 import androidx.compose.composer
 import androidx.ui.core.Text
 import androidx.ui.layout.Column
+import androidx.ui.layout.LayoutSize
 import androidx.ui.layout.Row
 
 import kotlin.random.Random
@@ -74,13 +75,13 @@
 
 @Composable
 fun Table(children: @Composable() () -> Unit) {
-    Column { children() }
+    Column(mainAxisSize = LayoutSize.Expand) { children() }
 }
 
 @Composable
 fun QueryColumn(query: Query) {
     // TODO: we could do some conditional styling here which would make the test better
-    Column {
+    Column(mainAxisSize = LayoutSize.Expand) {
         Text(text="${query.elapsed}")
         Text(text=query.query)
     }
@@ -91,9 +92,9 @@
     println(db)
     val columns = 5
     val topQueries = db.topQueries(columns)
-    Row {
-        Column { Text(text=db.name) }
-        Column { Text(text="${db.queries.size}") }
+    Row(mainAxisSize = LayoutSize.Expand) {
+        Column(mainAxisSize = LayoutSize.Expand) { Text(text=db.name) }
+        Column(mainAxisSize = LayoutSize.Expand) { Text(text="${db.queries.size}") }
         topQueries.forEach { query ->
             QueryColumn(query = query)
         }
diff --git a/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/deeptree/DeepTree.kt b/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/deeptree/DeepTree.kt
index 6347a30..c121e31 100644
--- a/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/deeptree/DeepTree.kt
+++ b/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/deeptree/DeepTree.kt
@@ -23,6 +23,7 @@
 import androidx.ui.foundation.ColoredRect
 import androidx.ui.graphics.Color
 import androidx.ui.layout.Column
+import androidx.ui.layout.LayoutSize
 import androidx.ui.layout.Row
 
 @Composable
@@ -38,9 +39,9 @@
 @Composable
 fun Stack(vertical: Boolean, children: @Composable() () -> Unit) {
     if (vertical) {
-        Column { children() }
+        Column(mainAxisSize = LayoutSize.Expand) { children() }
     } else {
-        Row { children() }
+        Row(mainAxisSize = LayoutSize.Expand) { children() }
     }
 }
 
diff --git a/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/siblings/SiblingManagement.kt b/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/siblings/SiblingManagement.kt
index 169994b..040d0b3 100644
--- a/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/siblings/SiblingManagement.kt
+++ b/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/siblings/SiblingManagement.kt
@@ -26,13 +26,14 @@
 import androidx.ui.foundation.ColoredRect
 import androidx.ui.graphics.Color
 import androidx.ui.layout.Column
+import androidx.ui.layout.LayoutSize
 import androidx.ui.layout.Row
 import androidx.ui.text.TextStyle
 import kotlin.random.Random
 
 @Composable
 fun Stack(children: @Composable() () -> Unit) {
-    Column {
+    Column(mainAxisSize = LayoutSize.Expand) {
         children()
     }
 }
@@ -44,7 +45,7 @@
         1 -> Color.Black
         else -> Color.Magenta
     }
-    Row {
+    Row(mainAxisSize = LayoutSize.Expand) {
         ColoredRect(color=color, width = 16.dp, height = 16.dp)
         Text(text="${item.id}", style = TextStyle(color=color))
     }
@@ -60,7 +61,7 @@
         1 -> Color.Black
         else -> Color.Magenta
     }
-    Row {
+    Row(mainAxisSize = LayoutSize.Expand) {
         ColoredRect(color=color, width = 16.dp, height = 16.dp)
         Text(text="${item.id}", style = TextStyle(color=color))
     }
diff --git a/core/core/api/1.2.0-alpha05.txt b/core/core/api/1.2.0-alpha05.txt
index 334f937..19f1ef5 100644
--- a/core/core/api/1.2.0-alpha05.txt
+++ b/core/core/api/1.2.0-alpha05.txt
@@ -277,11 +277,13 @@
 
   public static class NotificationCompat.Action {
     ctor public NotificationCompat.Action(int, CharSequence!, android.app.PendingIntent!);
+    ctor public NotificationCompat.Action(androidx.core.graphics.drawable.IconCompat?, CharSequence?, android.app.PendingIntent?);
     method public android.app.PendingIntent! getActionIntent();
     method public boolean getAllowGeneratedReplies();
     method public androidx.core.app.RemoteInput![]! getDataOnlyRemoteInputs();
     method public android.os.Bundle! getExtras();
-    method public int getIcon();
+    method @Deprecated public int getIcon();
+    method public androidx.core.graphics.drawable.IconCompat? getIconCompat();
     method public androidx.core.app.RemoteInput![]! getRemoteInputs();
     method @androidx.core.app.NotificationCompat.Action.SemanticAction public int getSemanticAction();
     method public boolean getShowsUserInterface();
@@ -299,11 +301,12 @@
     field public static final int SEMANTIC_ACTION_THUMBS_UP = 8; // 0x8
     field public static final int SEMANTIC_ACTION_UNMUTE = 7; // 0x7
     field public android.app.PendingIntent! actionIntent;
-    field public int icon;
+    field @Deprecated public int icon;
     field public CharSequence! title;
   }
 
   public static final class NotificationCompat.Action.Builder {
+    ctor public NotificationCompat.Action.Builder(androidx.core.graphics.drawable.IconCompat?, CharSequence?, android.app.PendingIntent?);
     ctor public NotificationCompat.Action.Builder(int, CharSequence!, android.app.PendingIntent!);
     ctor public NotificationCompat.Action.Builder(androidx.core.app.NotificationCompat.Action!);
     method public androidx.core.app.NotificationCompat.Action.Builder! addExtras(android.os.Bundle!);
diff --git a/core/core/api/current.txt b/core/core/api/current.txt
index 334f937..19f1ef5 100644
--- a/core/core/api/current.txt
+++ b/core/core/api/current.txt
@@ -277,11 +277,13 @@
 
   public static class NotificationCompat.Action {
     ctor public NotificationCompat.Action(int, CharSequence!, android.app.PendingIntent!);
+    ctor public NotificationCompat.Action(androidx.core.graphics.drawable.IconCompat?, CharSequence?, android.app.PendingIntent?);
     method public android.app.PendingIntent! getActionIntent();
     method public boolean getAllowGeneratedReplies();
     method public androidx.core.app.RemoteInput![]! getDataOnlyRemoteInputs();
     method public android.os.Bundle! getExtras();
-    method public int getIcon();
+    method @Deprecated public int getIcon();
+    method public androidx.core.graphics.drawable.IconCompat? getIconCompat();
     method public androidx.core.app.RemoteInput![]! getRemoteInputs();
     method @androidx.core.app.NotificationCompat.Action.SemanticAction public int getSemanticAction();
     method public boolean getShowsUserInterface();
@@ -299,11 +301,12 @@
     field public static final int SEMANTIC_ACTION_THUMBS_UP = 8; // 0x8
     field public static final int SEMANTIC_ACTION_UNMUTE = 7; // 0x7
     field public android.app.PendingIntent! actionIntent;
-    field public int icon;
+    field @Deprecated public int icon;
     field public CharSequence! title;
   }
 
   public static final class NotificationCompat.Action.Builder {
+    ctor public NotificationCompat.Action.Builder(androidx.core.graphics.drawable.IconCompat?, CharSequence?, android.app.PendingIntent?);
     ctor public NotificationCompat.Action.Builder(int, CharSequence!, android.app.PendingIntent!);
     ctor public NotificationCompat.Action.Builder(androidx.core.app.NotificationCompat.Action!);
     method public androidx.core.app.NotificationCompat.Action.Builder! addExtras(android.os.Bundle!);
diff --git a/core/core/api/public_plus_experimental_1.2.0-alpha05.txt b/core/core/api/public_plus_experimental_1.2.0-alpha05.txt
index 334f937..19f1ef5 100644
--- a/core/core/api/public_plus_experimental_1.2.0-alpha05.txt
+++ b/core/core/api/public_plus_experimental_1.2.0-alpha05.txt
@@ -277,11 +277,13 @@
 
   public static class NotificationCompat.Action {
     ctor public NotificationCompat.Action(int, CharSequence!, android.app.PendingIntent!);
+    ctor public NotificationCompat.Action(androidx.core.graphics.drawable.IconCompat?, CharSequence?, android.app.PendingIntent?);
     method public android.app.PendingIntent! getActionIntent();
     method public boolean getAllowGeneratedReplies();
     method public androidx.core.app.RemoteInput![]! getDataOnlyRemoteInputs();
     method public android.os.Bundle! getExtras();
-    method public int getIcon();
+    method @Deprecated public int getIcon();
+    method public androidx.core.graphics.drawable.IconCompat? getIconCompat();
     method public androidx.core.app.RemoteInput![]! getRemoteInputs();
     method @androidx.core.app.NotificationCompat.Action.SemanticAction public int getSemanticAction();
     method public boolean getShowsUserInterface();
@@ -299,11 +301,12 @@
     field public static final int SEMANTIC_ACTION_THUMBS_UP = 8; // 0x8
     field public static final int SEMANTIC_ACTION_UNMUTE = 7; // 0x7
     field public android.app.PendingIntent! actionIntent;
-    field public int icon;
+    field @Deprecated public int icon;
     field public CharSequence! title;
   }
 
   public static final class NotificationCompat.Action.Builder {
+    ctor public NotificationCompat.Action.Builder(androidx.core.graphics.drawable.IconCompat?, CharSequence?, android.app.PendingIntent?);
     ctor public NotificationCompat.Action.Builder(int, CharSequence!, android.app.PendingIntent!);
     ctor public NotificationCompat.Action.Builder(androidx.core.app.NotificationCompat.Action!);
     method public androidx.core.app.NotificationCompat.Action.Builder! addExtras(android.os.Bundle!);
diff --git a/core/core/api/public_plus_experimental_current.txt b/core/core/api/public_plus_experimental_current.txt
index 334f937..19f1ef5 100644
--- a/core/core/api/public_plus_experimental_current.txt
+++ b/core/core/api/public_plus_experimental_current.txt
@@ -277,11 +277,13 @@
 
   public static class NotificationCompat.Action {
     ctor public NotificationCompat.Action(int, CharSequence!, android.app.PendingIntent!);
+    ctor public NotificationCompat.Action(androidx.core.graphics.drawable.IconCompat?, CharSequence?, android.app.PendingIntent?);
     method public android.app.PendingIntent! getActionIntent();
     method public boolean getAllowGeneratedReplies();
     method public androidx.core.app.RemoteInput![]! getDataOnlyRemoteInputs();
     method public android.os.Bundle! getExtras();
-    method public int getIcon();
+    method @Deprecated public int getIcon();
+    method public androidx.core.graphics.drawable.IconCompat? getIconCompat();
     method public androidx.core.app.RemoteInput![]! getRemoteInputs();
     method @androidx.core.app.NotificationCompat.Action.SemanticAction public int getSemanticAction();
     method public boolean getShowsUserInterface();
@@ -299,11 +301,12 @@
     field public static final int SEMANTIC_ACTION_THUMBS_UP = 8; // 0x8
     field public static final int SEMANTIC_ACTION_UNMUTE = 7; // 0x7
     field public android.app.PendingIntent! actionIntent;
-    field public int icon;
+    field @Deprecated public int icon;
     field public CharSequence! title;
   }
 
   public static final class NotificationCompat.Action.Builder {
+    ctor public NotificationCompat.Action.Builder(androidx.core.graphics.drawable.IconCompat?, CharSequence?, android.app.PendingIntent?);
     ctor public NotificationCompat.Action.Builder(int, CharSequence!, android.app.PendingIntent!);
     ctor public NotificationCompat.Action.Builder(androidx.core.app.NotificationCompat.Action!);
     method public androidx.core.app.NotificationCompat.Action.Builder! addExtras(android.os.Bundle!);
diff --git a/core/core/api/restricted_1.2.0-alpha05.txt b/core/core/api/restricted_1.2.0-alpha05.txt
index 2108b6ba..00ed3ab 100644
--- a/core/core/api/restricted_1.2.0-alpha05.txt
+++ b/core/core/api/restricted_1.2.0-alpha05.txt
@@ -322,11 +322,13 @@
 
   public static class NotificationCompat.Action {
     ctor public NotificationCompat.Action(int, CharSequence!, android.app.PendingIntent!);
+    ctor public NotificationCompat.Action(androidx.core.graphics.drawable.IconCompat?, CharSequence?, android.app.PendingIntent?);
     method public android.app.PendingIntent! getActionIntent();
     method public boolean getAllowGeneratedReplies();
     method public androidx.core.app.RemoteInput![]! getDataOnlyRemoteInputs();
     method public android.os.Bundle! getExtras();
-    method public int getIcon();
+    method @Deprecated public int getIcon();
+    method public androidx.core.graphics.drawable.IconCompat? getIconCompat();
     method public androidx.core.app.RemoteInput![]! getRemoteInputs();
     method @androidx.core.app.NotificationCompat.Action.SemanticAction public int getSemanticAction();
     method public boolean getShowsUserInterface();
@@ -344,11 +346,12 @@
     field public static final int SEMANTIC_ACTION_THUMBS_UP = 8; // 0x8
     field public static final int SEMANTIC_ACTION_UNMUTE = 7; // 0x7
     field public android.app.PendingIntent! actionIntent;
-    field public int icon;
+    field @Deprecated public int icon;
     field public CharSequence! title;
   }
 
   public static final class NotificationCompat.Action.Builder {
+    ctor public NotificationCompat.Action.Builder(androidx.core.graphics.drawable.IconCompat?, CharSequence?, android.app.PendingIntent?);
     ctor public NotificationCompat.Action.Builder(int, CharSequence!, android.app.PendingIntent!);
     ctor public NotificationCompat.Action.Builder(androidx.core.app.NotificationCompat.Action!);
     method public androidx.core.app.NotificationCompat.Action.Builder! addExtras(android.os.Bundle!);
diff --git a/core/core/api/restricted_current.txt b/core/core/api/restricted_current.txt
index 2108b6ba..00ed3ab 100644
--- a/core/core/api/restricted_current.txt
+++ b/core/core/api/restricted_current.txt
@@ -322,11 +322,13 @@
 
   public static class NotificationCompat.Action {
     ctor public NotificationCompat.Action(int, CharSequence!, android.app.PendingIntent!);
+    ctor public NotificationCompat.Action(androidx.core.graphics.drawable.IconCompat?, CharSequence?, android.app.PendingIntent?);
     method public android.app.PendingIntent! getActionIntent();
     method public boolean getAllowGeneratedReplies();
     method public androidx.core.app.RemoteInput![]! getDataOnlyRemoteInputs();
     method public android.os.Bundle! getExtras();
-    method public int getIcon();
+    method @Deprecated public int getIcon();
+    method public androidx.core.graphics.drawable.IconCompat? getIconCompat();
     method public androidx.core.app.RemoteInput![]! getRemoteInputs();
     method @androidx.core.app.NotificationCompat.Action.SemanticAction public int getSemanticAction();
     method public boolean getShowsUserInterface();
@@ -344,11 +346,12 @@
     field public static final int SEMANTIC_ACTION_THUMBS_UP = 8; // 0x8
     field public static final int SEMANTIC_ACTION_UNMUTE = 7; // 0x7
     field public android.app.PendingIntent! actionIntent;
-    field public int icon;
+    field @Deprecated public int icon;
     field public CharSequence! title;
   }
 
   public static final class NotificationCompat.Action.Builder {
+    ctor public NotificationCompat.Action.Builder(androidx.core.graphics.drawable.IconCompat?, CharSequence?, android.app.PendingIntent?);
     ctor public NotificationCompat.Action.Builder(int, CharSequence!, android.app.PendingIntent!);
     ctor public NotificationCompat.Action.Builder(androidx.core.app.NotificationCompat.Action!);
     method public androidx.core.app.NotificationCompat.Action.Builder! addExtras(android.os.Bundle!);
diff --git a/core/core/src/androidTest/java/androidx/core/app/NotificationCompatTest.java b/core/core/src/androidTest/java/androidx/core/app/NotificationCompatTest.java
index f818a303..a79e27d 100644
--- a/core/core/src/androidTest/java/androidx/core/app/NotificationCompatTest.java
+++ b/core/core/src/androidTest/java/androidx/core/app/NotificationCompatTest.java
@@ -173,6 +173,17 @@
     }
 
     @Test
+    public void testNotificationActionBuilder_copiesIcon() {
+        NotificationCompat.Action a = new NotificationCompat.Action.Builder(
+                R.drawable.notification_action_background, "title", null).build();
+        assertEquals(R.drawable.notification_action_background, a.getIconCompat().getResId());
+
+        NotificationCompat.Action aCopy = new NotificationCompat.Action.Builder(a).build();
+
+        assertEquals(R.drawable.notification_action_background, aCopy.getIconCompat().getResId());
+    }
+
+    @Test
     public void testNotificationActionBuilder_copiesRemoteInputs() throws Throwable {
         NotificationCompat.Action a = newActionBuilder()
                 .addRemoteInput(new RemoteInput("a", "b", null, false,
@@ -250,6 +261,20 @@
 
     @SdkSuppress(minSdkVersion = 20)
     @Test
+    public void testGetActionCompatFromAction_sameIconResourceId() {
+        NotificationCompat.Action action = new NotificationCompat.Action.Builder(
+                R.drawable.notification_bg, "title", null).build();
+        assertEquals(R.drawable.notification_bg, action.getIconCompat().getResId());
+        Notification notification = newNotificationBuilder().addAction(action).build();
+
+        NotificationCompat.Action result =
+                NotificationCompat.getActionCompatFromAction(notification.actions[0]);
+
+        assertEquals(R.drawable.notification_bg, result.getIconCompat().getResId());
+    }
+
+    @SdkSuppress(minSdkVersion = 20)
+    @Test
     public void testGetActionCompatFromAction_showsUserInterface() {
         NotificationCompat.Action action = newActionBuilder()
                 .setShowsUserInterface(false).build();
@@ -906,14 +931,14 @@
     @Test
     public void action_builder_hasDefault() {
         NotificationCompat.Action action =
-                new NotificationCompat.Action.Builder(0, "Test Title", null).build();
+                newActionBuilder().build();
         assertEquals(NotificationCompat.Action.SEMANTIC_ACTION_NONE, action.getSemanticAction());
     }
 
     @Test
     public void action_builder_setSemanticAction() {
         NotificationCompat.Action action =
-                new NotificationCompat.Action.Builder(0, "Test Title", null)
+                newActionBuilder()
                         .setSemanticAction(NotificationCompat.Action.SEMANTIC_ACTION_REPLY)
                         .build();
         assertEquals(NotificationCompat.Action.SEMANTIC_ACTION_REPLY, action.getSemanticAction());
@@ -923,7 +948,7 @@
     @SdkSuppress(minSdkVersion = 20)
     public void action_semanticAction_toAndFromNotification() {
         NotificationCompat.Action action =
-                new NotificationCompat.Action.Builder(0, "Test Title", null)
+                newActionBuilder()
                         .setSemanticAction(NotificationCompat.Action.SEMANTIC_ACTION_REPLY)
                         .build();
         Notification notification = newNotificationBuilder().addAction(action).build();
@@ -933,7 +958,7 @@
     }
 
     private static final NotificationCompat.Action TEST_INVISIBLE_ACTION =
-            new NotificationCompat.Action.Builder(0, "Test Title", null)
+            newActionBuilder()
                     .setSemanticAction(NotificationCompat.Action.SEMANTIC_ACTION_MUTE)
                     .setShowsUserInterface(false)
                     .build();
@@ -969,9 +994,7 @@
 
     @Test
     public void action_builder_defaultNotContextual() {
-        NotificationCompat.Action action =
-                new NotificationCompat.Action.Builder(0, "Test Title", null)
-                        .build();
+        NotificationCompat.Action action = newActionBuilder().build();
         assertFalse(action.isContextual());
     }
 
@@ -989,9 +1012,7 @@
 
     @Test
     public void action_builder_contextual_invalidIntentCausesNpe() {
-        NotificationCompat.Action.Builder builder =
-                new NotificationCompat.Action.Builder(0, "Test Title", null)
-                        .setContextual(true);
+        NotificationCompat.Action.Builder builder = newActionBuilder().setContextual(true);
         try {
             builder.build();
             fail("Creating a contextual Action with a null PendingIntent should cause a "
@@ -1008,8 +1029,8 @@
         // Without a PendingIntent the Action.Builder class throws an NPE when building a contextual
         // action.
         PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
-        NotificationCompat.Action action =
-                new NotificationCompat.Action.Builder(0, "Test Title", pendingIntent)
+        NotificationCompat.Action action = new NotificationCompat.Action.Builder(
+                R.drawable.notification_bg, "Test Title", pendingIntent)
                         .setContextual(true)
                         .build();
         Notification notification = newNotificationBuilder().addAction(action).build();
diff --git a/core/core/src/main/java/androidx/core/app/NotificationCompat.java b/core/core/src/main/java/androidx/core/app/NotificationCompat.java
index 9b5b35d..8eaf28a 100644
--- a/core/core/src/main/java/androidx/core/app/NotificationCompat.java
+++ b/core/core/src/main/java/androidx/core/app/NotificationCompat.java
@@ -36,6 +36,7 @@
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffColorFilter;
 import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
 import android.media.AudioAttributes;
 import android.media.AudioManager;
 import android.net.Uri;
@@ -1970,15 +1971,31 @@
         }
 
         /**
+         * Create a bitmap using the given icon together with a color filter created from the given
+         * color.
+         *
          * @hide
          */
         @RestrictTo(LIBRARY_GROUP_PREFIX)
         public Bitmap createColoredBitmap(int iconId, int color) {
-            return createColoredBitmap(iconId, color, 0);
+            return createColoredBitmap(iconId, color, 0 /* size */);
+        }
+
+        /**
+         * Create a bitmap using the given icon together with a color filter created from the given
+         * color.
+         */
+        Bitmap createColoredBitmap(IconCompat icon, int color) {
+            return createColoredBitmap(icon, color, 0 /* size */);
         }
 
         private Bitmap createColoredBitmap(int iconId, int color, int size) {
-            Drawable drawable = mBuilder.mContext.getResources().getDrawable(iconId);
+            return createColoredBitmap(IconCompat.createWithResource(mBuilder.mContext, iconId),
+                    color, size);
+        }
+
+        private Bitmap createColoredBitmap(IconCompat icon, int color, int size) {
+            Drawable drawable = icon.loadDrawable(mBuilder.mContext);
             int width = size == 0 ? drawable.getIntrinsicWidth() : size;
             int height = size == 0 ? drawable.getIntrinsicHeight() : size;
             Bitmap resultBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
@@ -3138,7 +3155,7 @@
                     tombstone ? R.layout.notification_action_tombstone
                             : R.layout.notification_action);
             button.setImageViewBitmap(R.id.action_image,
-                    createColoredBitmap(action.getIcon(), mBuilder.mContext.getResources()
+                    createColoredBitmap(action.getIconCompat(), mBuilder.mContext.getResources()
                             .getColor(R.color.notification_action_color_filter)));
             button.setTextViewText(R.id.action_text, action.title);
             if (!tombstone) {
@@ -3227,6 +3244,7 @@
         static final String EXTRA_SEMANTIC_ACTION = "android.support.action.semanticAction";
 
         final Bundle mExtras;
+        @Nullable private IconCompat mIcon;
         private final RemoteInput[] mRemoteInputs;
 
         /**
@@ -3249,7 +3267,10 @@
 
         /**
          * Small icon representing the action.
+         *
+         * @deprecated Use {@link #getIconCompat()} instead.
          */
+        @Deprecated
         public int icon;
         /**
          * Title of the action.
@@ -3262,15 +3283,38 @@
         public PendingIntent actionIntent;
 
         public Action(int icon, CharSequence title, PendingIntent intent) {
+            this(icon == 0 ? null : IconCompat.createWithResource(null, "", icon), title, intent);
+        }
+
+        /**
+         * <strong>Note:</strong> For devices running an Android version strictly lower than API
+         * level 23 this constructor only supports resource-ID based IconCompat objects.
+         */
+        public Action(@Nullable IconCompat icon, @Nullable CharSequence title,
+                @Nullable PendingIntent intent) {
             this(icon, title, intent, new Bundle(), null, null, true, SEMANTIC_ACTION_NONE, true,
-                false /* isContextual */);
+                    false /* isContextual */);
         }
 
         Action(int icon, CharSequence title, PendingIntent intent, Bundle extras,
                 RemoteInput[] remoteInputs, RemoteInput[] dataOnlyRemoteInputs,
                 boolean allowGeneratedReplies, @SemanticAction int semanticAction,
                 boolean showsUserInterface, boolean isContextual) {
-            this.icon = icon;
+            this(icon == 0 ? null : IconCompat.createWithResource(null, "", icon), title,
+                    intent, extras, remoteInputs, dataOnlyRemoteInputs, allowGeneratedReplies,
+                    semanticAction, showsUserInterface, isContextual);
+        }
+
+        // Package private access to avoid adding a SyntheticAccessor for the Action.Builder class.
+        Action(@Nullable IconCompat icon, CharSequence title, PendingIntent intent,
+                Bundle extras,
+                RemoteInput[] remoteInputs, RemoteInput[] dataOnlyRemoteInputs,
+                boolean allowGeneratedReplies, @SemanticAction int semanticAction,
+                boolean showsUserInterface, boolean isContextual) {
+            this.mIcon = icon;
+            if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
+                this.icon = icon.getResId();
+            }
             this.title = NotificationCompat.Builder.limitCharSequenceLength(title);
             this.actionIntent = intent;
             this.mExtras = extras != null ? extras : new Bundle();
@@ -3282,10 +3326,24 @@
             this.mIsContextual = isContextual;
         }
 
+        /**
+         * @deprecated use {@link #getIconCompat()} instead.
+         */
+        @Deprecated
         public int getIcon() {
             return icon;
         }
 
+        /**
+         * Return the icon associated with this Action.
+         */
+        public @Nullable IconCompat getIconCompat() {
+            if (mIcon == null && icon != 0) {
+                mIcon = IconCompat.createWithResource(null, "", icon);
+            }
+            return mIcon;
+        }
+
         public CharSequence getTitle() {
             return title;
         }
@@ -3364,7 +3422,7 @@
          * Builder class for {@link Action} objects.
          */
         public static final class Builder {
-            private final int mIcon;
+            private final IconCompat mIcon;
             private final CharSequence mTitle;
             private final PendingIntent mIntent;
             private boolean mAllowGeneratedReplies = true;
@@ -3376,12 +3434,33 @@
 
             /**
              * Construct a new builder for {@link Action} object.
+             *
+             * <p><strong>Note:</strong> For devices running an Android version strictly lower than
+             * API level 23 this constructor only supports resource-ID based IconCompat objects.
+             * @param icon icon to show for this action
+             * @param title the title of the action
+             * @param intent the {@link PendingIntent} to fire when users trigger this action
+             */
+            public Builder(@Nullable IconCompat icon, @Nullable CharSequence title,
+                    @Nullable PendingIntent intent) {
+                this(icon, title, intent, new Bundle(), null, true, SEMANTIC_ACTION_NONE, true,
+                        false /* isContextual */);
+            }
+
+            /**
+             * Construct a new builder for {@link Action} object.
              * @param icon icon to show for this action
              * @param title the title of the action
              * @param intent the {@link PendingIntent} to fire when users trigger this action
              */
             public Builder(int icon, CharSequence title, PendingIntent intent) {
-                this(icon, title, intent, new Bundle(), null, true, SEMANTIC_ACTION_NONE, true,
+                this(icon == 0 ? null : IconCompat.createWithResource(null, "", icon), title,
+                        intent,
+                        new Bundle(),
+                        null,
+                        true,
+                        SEMANTIC_ACTION_NONE,
+                        true,
                         false /* isContextual */);
             }
 
@@ -3391,13 +3470,15 @@
              * @param action the action to read fields from.
              */
             public Builder(Action action) {
-                this(action.icon, action.title, action.actionIntent, new Bundle(action.mExtras),
+                this(action.getIconCompat(), action.title, action.actionIntent,
+                        new Bundle(action.mExtras),
                         action.getRemoteInputs(), action.getAllowGeneratedReplies(),
                         action.getSemanticAction(), action.mShowsUserInterface,
                         action.isContextual());
             }
 
-            private Builder(int icon, CharSequence title, PendingIntent intent, Bundle extras,
+            private Builder(@Nullable IconCompat icon, CharSequence title, PendingIntent intent,
+                    Bundle extras,
                     RemoteInput[] remoteInputs, boolean allowGeneratedReplies,
                     @SemanticAction int semanticAction, boolean showsUserInterface,
                     boolean isContextual) {
@@ -4172,9 +4253,17 @@
 
         @RequiresApi(20)
         private static Notification.Action getActionFromActionCompat(Action actionCompat) {
-            Notification.Action.Builder actionBuilder = new Notification.Action.Builder(
-                    actionCompat.getIcon(), actionCompat.getTitle(),
-                    actionCompat.getActionIntent());
+            Notification.Action.Builder actionBuilder;
+            if (Build.VERSION.SDK_INT >= 23) {
+                IconCompat iconCompat = actionCompat.getIconCompat();
+                actionBuilder = new Notification.Action.Builder(
+                        iconCompat == null ? null : iconCompat.toIcon(), actionCompat.getTitle(),
+                        actionCompat.getActionIntent());
+            } else {
+                actionBuilder = new Notification.Action.Builder(
+                        actionCompat.getIcon(), actionCompat.getTitle(),
+                        actionCompat.getActionIntent());
+            }
             Bundle actionExtras;
             if (actionCompat.getExtras() != null) {
                 actionExtras = new Bundle(actionCompat.getExtras());
diff --git a/core/core/src/main/java/androidx/core/app/NotificationCompatBuilder.java b/core/core/src/main/java/androidx/core/app/NotificationCompatBuilder.java
index 1642870..3711994 100644
--- a/core/core/src/main/java/androidx/core/app/NotificationCompatBuilder.java
+++ b/core/core/src/main/java/androidx/core/app/NotificationCompatBuilder.java
@@ -32,6 +32,7 @@
 import android.widget.RemoteViews;
 
 import androidx.annotation.RestrictTo;
+import androidx.core.graphics.drawable.IconCompat;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -260,8 +261,16 @@
 
     private void addAction(NotificationCompat.Action action) {
         if (Build.VERSION.SDK_INT >= 20) {
-            Notification.Action.Builder actionBuilder = new Notification.Action.Builder(
-                    action.getIcon(), action.getTitle(), action.getActionIntent());
+            Notification.Action.Builder actionBuilder;
+            if (Build.VERSION.SDK_INT >= 23) {
+                IconCompat iconCompat = action.getIconCompat();
+                actionBuilder = new Notification.Action.Builder(
+                        iconCompat == null ? null : iconCompat.toIcon(), action.getTitle(),
+                                action.getActionIntent());
+            } else {
+                actionBuilder = new Notification.Action.Builder(
+                        action.getIcon(), action.getTitle(), action.getActionIntent());
+            }
             if (action.getRemoteInputs() != null) {
                 for (android.app.RemoteInput remoteInput : RemoteInput.fromCompat(
                         action.getRemoteInputs())) {
diff --git a/fragment/fragment/src/androidTest/java/androidx/fragment/app/UserVisibleHintTest.kt b/fragment/fragment/src/androidTest/java/androidx/fragment/app/UserVisibleHintTest.kt
new file mode 100644
index 0000000..1504176
--- /dev/null
+++ b/fragment/fragment/src/androidTest/java/androidx/fragment/app/UserVisibleHintTest.kt
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2019 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.fragment.app
+
+import androidx.fragment.app.test.FragmentTestActivity
+import androidx.test.annotation.UiThreadTest
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import androidx.test.rule.ActivityTestRule
+import androidx.testutils.runOnUiThreadRethrow
+import com.google.common.truth.Truth.assertWithMessage
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@Suppress("DEPRECATION")
+@RunWith(AndroidJUnit4::class)
+@MediumTest
+class UserVisibleHintTest {
+
+    @get:Rule
+    var activityRule = ActivityTestRule(FragmentTestActivity::class.java)
+
+    @UiThreadTest
+    @Test
+    fun startOrdering() {
+        var firstStartedFragment: Fragment? = null
+        val callback: (fragment: Fragment) -> Unit = {
+            if (firstStartedFragment == null) {
+                firstStartedFragment = it
+            }
+        }
+        val deferredFragment = ReportStartFragment(callback).apply {
+            userVisibleHint = false
+        }
+        val fragment = ReportStartFragment(callback)
+        val fm = activityRule.activity.supportFragmentManager
+        fm.beginTransaction()
+            .add(deferredFragment, "deferred")
+            .commit()
+        fm.beginTransaction()
+            .add(fragment, "fragment")
+            .commit()
+        activityRule.executePendingTransactions()
+
+        assertWithMessage("userVisibleHint=false Fragments should start after other Fragments")
+            .that(firstStartedFragment)
+            .isSameInstanceAs(fragment)
+    }
+
+    @Test
+    fun startOrderingAfterSave() {
+        var firstStartedFragment: Fragment? = null
+        val callback: (fragment: Fragment) -> Unit = {
+            if (firstStartedFragment == null) {
+                firstStartedFragment = it
+            }
+        }
+        val fm = activityRule.activity.supportFragmentManager
+
+        // Add the fragment, save its state, then remove it.
+        var deferredFragment = ReportStartFragment().apply {
+            userVisibleHint = false
+        }
+        fm.beginTransaction().add(deferredFragment, "tag").commit()
+        activityRule.executePendingTransactions()
+        var state: Fragment.SavedState? = null
+        activityRule.runOnUiThreadRethrow {
+            state = fm.saveFragmentInstanceState(deferredFragment)
+        }
+        fm.beginTransaction().remove(deferredFragment).commit()
+        activityRule.executePendingTransactions()
+
+        // Create a new instance, calling setInitialSavedState
+        deferredFragment = ReportStartFragment(callback)
+        deferredFragment.setInitialSavedState(state)
+
+        val fragment = ReportStartFragment(callback)
+        fm.beginTransaction()
+            .add(deferredFragment, "deferred")
+            .commit()
+        fm.beginTransaction()
+            .add(fragment, "fragment")
+            .commit()
+        activityRule.executePendingTransactions()
+
+        assertWithMessage("userVisibleHint=false Fragments should start after other Fragments")
+            .that(firstStartedFragment)
+            .isSameInstanceAs(fragment)
+    }
+
+    class ReportStartFragment(
+        val callback: ((fragment: Fragment) -> Unit) = {}
+    ) : StrictFragment() {
+        override fun onStart() {
+            super.onStart()
+            callback.invoke(this)
+        }
+    }
+}
diff --git a/mediarouter/src/main/res/layout/mr_cast_media_metadata.xml b/mediarouter/src/main/res/layout/mr_cast_media_metadata.xml
index a97fad4..fa4ac44 100644
--- a/mediarouter/src/main/res/layout/mr_cast_media_metadata.xml
+++ b/mediarouter/src/main/res/layout/mr_cast_media_metadata.xml
@@ -38,6 +38,7 @@
                      android:layout_width="24dp"
                      android:layout_height="24dp"
                      android:layout_gravity="center_vertical"
+                     android:contentDescription="@string/mr_controller_close_description"
                      android:src="?attr/mediaRouteCloseDrawable"
                      android:background="?attr/selectableItemBackgroundBorderless" />
         <LinearLayout android:layout_width="match_parent"
diff --git a/mediarouter/src/main/res/layout/mr_controller_material_dialog_b.xml b/mediarouter/src/main/res/layout/mr_controller_material_dialog_b.xml
index b7f1cfc..bf5756f 100644
--- a/mediarouter/src/main/res/layout/mr_controller_material_dialog_b.xml
+++ b/mediarouter/src/main/res/layout/mr_controller_material_dialog_b.xml
@@ -40,6 +40,7 @@
                       android:singleLine="true"
                       android:ellipsize="end"
                       android:textAlignment="viewStart"
+                      android:importantForAccessibility="no"
                       android:textAppearance="@style/TextAppearance.MediaRouter.Title" />
             <ImageButton android:id="@+id/mr_close"
                          android:layout_width="48dp"
diff --git a/mediarouter/src/main/res/layout/mr_picker_dialog.xml b/mediarouter/src/main/res/layout/mr_picker_dialog.xml
index 640c96d..b988df7 100644
--- a/mediarouter/src/main/res/layout/mr_picker_dialog.xml
+++ b/mediarouter/src/main/res/layout/mr_picker_dialog.xml
@@ -29,6 +29,7 @@
                      android:layout_width="24dp"
                      android:layout_height="24dp"
                      android:layout_gravity="center_vertical"
+                     android:contentDescription="@string/mr_controller_close_description"
                      android:src="?attr/mediaRouteCloseDrawable"
                      android:background="?attr/selectableItemBackgroundBorderless" />
     </LinearLayout>
diff --git a/navigation/navigation-common-ktx/api/api_lint.ignore b/navigation/navigation-common-ktx/api/api_lint.ignore
index 6984790..83b4fd0 100644
--- a/navigation/navigation-common-ktx/api/api_lint.ignore
+++ b/navigation/navigation-common-ktx/api/api_lint.ignore
@@ -3,3 +3,17 @@
     Method NavArgumentBuilder.getType appears to be throwing java.lang.IllegalStateException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
 DocumentExceptions: androidx.navigation.NavGraphBuilder#build():
     Method NavGraphBuilder.build appears to be throwing java.lang.IllegalStateException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
+
+
+SetterReturnsThis: androidx.navigation.AnimBuilder:
+    Methods must return the builder object (return type AnimBuilder instead of void): method androidx.navigation.AnimBuilder.setEnter(int)
+SetterReturnsThis: androidx.navigation.NavActionBuilder:
+    Methods must return the builder object (return type NavActionBuilder instead of void): method androidx.navigation.NavActionBuilder.setDestinationId(int)
+SetterReturnsThis: androidx.navigation.NavArgumentBuilder:
+    Methods must return the builder object (return type NavArgumentBuilder instead of void): method androidx.navigation.NavArgumentBuilder.setType(androidx.navigation.NavType<?>)
+SetterReturnsThis: androidx.navigation.NavDestinationBuilder:
+    Methods must return the builder object (return type NavDestinationBuilder instead of void): method androidx.navigation.NavDestinationBuilder.setLabel(CharSequence)
+SetterReturnsThis: androidx.navigation.NavOptionsBuilder:
+    Methods must return the builder object (return type NavOptionsBuilder instead of void): method androidx.navigation.NavOptionsBuilder.setLaunchSingleTop(boolean)
+SetterReturnsThis: androidx.navigation.PopUpToBuilder:
+    Methods must return the builder object (return type PopUpToBuilder instead of void): method androidx.navigation.PopUpToBuilder.setInclusive(boolean)
diff --git a/paging/common/api/3.0.0-alpha01.txt b/paging/common/api/3.0.0-alpha01.txt
index ed2cabf..943ab14 100644
--- a/paging/common/api/3.0.0-alpha01.txt
+++ b/paging/common/api/3.0.0-alpha01.txt
@@ -4,7 +4,6 @@
   public abstract class DataSource<Key, Value> {
     method @AnyThread public void addInvalidatedCallback(androidx.paging.DataSource.InvalidatedCallback onInvalidatedCallback);
     method @AnyThread public final void addInvalidatedCallback(kotlin.jvm.functions.Function0<kotlin.Unit> onInvalidatedCallback);
-    method protected final java.util.concurrent.Executor getExecutor();
     method @AnyThread public void invalidate();
     method @WorkerThread public boolean isInvalid();
     method public abstract suspend Object load$lintWithKotlin(androidx.paging.DataSource.Params<Key> params, kotlin.coroutines.Continuation<? super androidx.paging.DataSource.BaseResult<Value>> p);
@@ -14,20 +13,9 @@
     method public <ToValue> androidx.paging.DataSource<Key,ToValue> mapByPage(kotlin.jvm.functions.Function1<? super java.util.List<? extends Value>,? extends java.util.List<? extends ToValue>> function);
     method @AnyThread public void removeInvalidatedCallback(androidx.paging.DataSource.InvalidatedCallback onInvalidatedCallback);
     method @AnyThread public final void removeInvalidatedCallback(kotlin.jvm.functions.Function0<kotlin.Unit> onInvalidatedCallback);
-    property protected final java.util.concurrent.Executor executor;
     property @WorkerThread public boolean isInvalid;
   }
 
-  public static class DataSource.BaseResult<Value> {
-    method public final boolean getCounted();
-    method public final int getLeadingNulls();
-    method public final Object? getNextKey();
-    method public final int getOffset();
-    method public final Object? getPrevKey();
-    method public final int getTrailingNulls();
-    field public final java.util.List<Value> data;
-  }
-
   public abstract static class DataSource.Factory<Key, Value> {
     ctor public DataSource.Factory();
     method public abstract androidx.paging.DataSource<Key,Value> create();
@@ -41,13 +29,6 @@
     method @AnyThread public void onInvalidated();
   }
 
-  public static final class DataSource.Params<K> {
-    method public int getInitialLoadSize();
-    method public K? getKey();
-    method public int getPageSize();
-    method public boolean getPlaceholdersEnabled();
-  }
-
   public final class DataSourceKt {
     ctor public DataSourceKt();
   }
@@ -192,18 +173,17 @@
 
   @MainThread public abstract static class PagedList.BoundaryCallback<T> {
     ctor public PagedList.BoundaryCallback();
-    method public void onItemAtEndLoaded(T? itemAtEnd);
-    method public void onItemAtFrontLoaded(T? itemAtFront);
+    method public void onItemAtEndLoaded(T itemAtEnd);
+    method public void onItemAtFrontLoaded(T itemAtFront);
     method public void onZeroItemsLoaded();
   }
 
   public static final class PagedList.Builder<Key, Value> {
     ctor @Deprecated public PagedList.Builder(androidx.paging.DataSource<Key,Value> dataSource, androidx.paging.PagedList.Config config);
     ctor @Deprecated public PagedList.Builder(androidx.paging.DataSource<Key,Value> dataSource, int pageSize);
-    ctor public PagedList.Builder(androidx.paging.PagedSource<Key,Value> pagedSource, androidx.paging.PagedList.Config config);
-    ctor public PagedList.Builder(androidx.paging.PagedSource<Key,Value> pagedSource, int pageSize);
-    method @Deprecated @WorkerThread public androidx.paging.PagedList<Value> build();
-    method public suspend Object buildAsync(kotlin.coroutines.Continuation<? super androidx.paging.PagedList<Value>> p);
+    ctor public PagedList.Builder(androidx.paging.PagedSource<Key,Value> pagedSource, androidx.paging.PagedSource.LoadResult.Page<Key,Value> initialPage, androidx.paging.PagedList.Config config);
+    ctor public PagedList.Builder(androidx.paging.PagedSource<Key,Value> pagedSource, androidx.paging.PagedSource.LoadResult.Page<Key,Value> initialPage, int pageSize);
+    method public androidx.paging.PagedList<Value> build();
     method public androidx.paging.PagedList.Builder<Key,Value> setBoundaryCallback(androidx.paging.PagedList.BoundaryCallback<Value>? boundaryCallback);
     method public androidx.paging.PagedList.Builder<Key,Value> setCoroutineScope(kotlinx.coroutines.CoroutineScope coroutineScope);
     method public androidx.paging.PagedList.Builder<Key,Value> setFetchDispatcher(kotlinx.coroutines.CoroutineDispatcher fetchDispatcher);
@@ -292,24 +272,34 @@
     method public boolean getPlaceholdersEnabled();
   }
 
-  public static final class PagedSource.LoadResult<Key, Value> {
-    ctor public PagedSource.LoadResult(java.util.List<? extends Value> data, @IntRange(from=null) int itemsBefore, @IntRange(from=null) int itemsAfter, Key? nextKey, Key? prevKey);
+  public abstract static sealed class PagedSource.LoadResult<Key, Value> {
+  }
+
+  public static final class PagedSource.LoadResult.Error<Key, Value> extends androidx.paging.PagedSource.LoadResult<Key,Value> {
+    ctor public PagedSource.LoadResult.Error(Throwable throwable);
+    method public Throwable component1();
+    method public androidx.paging.PagedSource.LoadResult.Error<Key,Value> copy(Throwable throwable);
+    method public Throwable getThrowable();
+  }
+
+  public static final class PagedSource.LoadResult.Page<Key, Value> extends androidx.paging.PagedSource.LoadResult<Key,Value> {
+    ctor public PagedSource.LoadResult.Page(java.util.List<? extends Value> data, @IntRange(from=null) int itemsBefore, @IntRange(from=null) int itemsAfter, Key? nextKey, Key? prevKey);
     method public java.util.List<Value> component1();
     method public int component2();
     method public int component3();
     method public Key? component4();
     method public Key? component5();
-    method public androidx.paging.PagedSource.LoadResult<Key,Value> copy(java.util.List<? extends Value> data, int itemsBefore, int itemsAfter, Key? nextKey, Key? prevKey);
+    method public androidx.paging.PagedSource.LoadResult.Page<Key,Value> copy(java.util.List<? extends Value> data, int itemsBefore, int itemsAfter, Key? nextKey, Key? prevKey);
     method public java.util.List<Value> getData();
     method public int getItemsAfter();
     method public int getItemsBefore();
     method public Key? getNextKey();
     method public Key? getPrevKey();
     field public static final int COUNT_UNDEFINED = -1; // 0xffffffff
-    field public static final androidx.paging.PagedSource.LoadResult.Companion! Companion;
+    field public static final androidx.paging.PagedSource.LoadResult.Page.Companion! Companion;
   }
 
-  public static final class PagedSource.LoadResult.Companion {
+  public static final class PagedSource.LoadResult.Page.Companion {
   }
 
   public final class PagedSourceKt {
diff --git a/paging/common/api/api_lint.ignore b/paging/common/api/api_lint.ignore
index 2d2bc0a2..41ae48a 100644
--- a/paging/common/api/api_lint.ignore
+++ b/paging/common/api/api_lint.ignore
@@ -35,8 +35,8 @@
     Missing nullability on field `INSTANCE` in class `class androidx.paging.LoadState.Loading`
 MissingNullability: androidx.paging.PagedSource.KeyProvider.Positional#INSTANCE:
     Missing nullability on field `INSTANCE` in class `class androidx.paging.PagedSource.KeyProvider.Positional`
-MissingNullability: androidx.paging.PagedSource.LoadResult#Companion:
-    Missing nullability on field `Companion` in class `class androidx.paging.PagedSource.LoadResult`
+MissingNullability: androidx.paging.PagedSource.LoadResult.Page#Companion:
+    Missing nullability on field `Companion` in class `class androidx.paging.PagedSource.LoadResult.Page`
 
 
 RegistrationName: androidx.paging.DataSource#addInvalidatedCallback(androidx.paging.DataSource.InvalidatedCallback):
@@ -51,3 +51,7 @@
     Callback methods should be named register/unregister; was addWeakCallback
 RegistrationName: androidx.paging.PagedList#removeWeakCallback(androidx.paging.PagedList.Callback):
     Callback methods should be named register/unregister; was removeWeakCallback
+
+
+SetterReturnsThis: androidx.paging.PagedList.Builder:
+    Methods must return the builder object (return type Builder instead of androidx.paging.PagedList.Builder<Key,Value>): method androidx.paging.PagedList.Builder.setCoroutineScope(kotlinx.coroutines.CoroutineScope)
diff --git a/paging/common/api/current.txt b/paging/common/api/current.txt
index ed2cabf..943ab14 100644
--- a/paging/common/api/current.txt
+++ b/paging/common/api/current.txt
@@ -4,7 +4,6 @@
   public abstract class DataSource<Key, Value> {
     method @AnyThread public void addInvalidatedCallback(androidx.paging.DataSource.InvalidatedCallback onInvalidatedCallback);
     method @AnyThread public final void addInvalidatedCallback(kotlin.jvm.functions.Function0<kotlin.Unit> onInvalidatedCallback);
-    method protected final java.util.concurrent.Executor getExecutor();
     method @AnyThread public void invalidate();
     method @WorkerThread public boolean isInvalid();
     method public abstract suspend Object load$lintWithKotlin(androidx.paging.DataSource.Params<Key> params, kotlin.coroutines.Continuation<? super androidx.paging.DataSource.BaseResult<Value>> p);
@@ -14,20 +13,9 @@
     method public <ToValue> androidx.paging.DataSource<Key,ToValue> mapByPage(kotlin.jvm.functions.Function1<? super java.util.List<? extends Value>,? extends java.util.List<? extends ToValue>> function);
     method @AnyThread public void removeInvalidatedCallback(androidx.paging.DataSource.InvalidatedCallback onInvalidatedCallback);
     method @AnyThread public final void removeInvalidatedCallback(kotlin.jvm.functions.Function0<kotlin.Unit> onInvalidatedCallback);
-    property protected final java.util.concurrent.Executor executor;
     property @WorkerThread public boolean isInvalid;
   }
 
-  public static class DataSource.BaseResult<Value> {
-    method public final boolean getCounted();
-    method public final int getLeadingNulls();
-    method public final Object? getNextKey();
-    method public final int getOffset();
-    method public final Object? getPrevKey();
-    method public final int getTrailingNulls();
-    field public final java.util.List<Value> data;
-  }
-
   public abstract static class DataSource.Factory<Key, Value> {
     ctor public DataSource.Factory();
     method public abstract androidx.paging.DataSource<Key,Value> create();
@@ -41,13 +29,6 @@
     method @AnyThread public void onInvalidated();
   }
 
-  public static final class DataSource.Params<K> {
-    method public int getInitialLoadSize();
-    method public K? getKey();
-    method public int getPageSize();
-    method public boolean getPlaceholdersEnabled();
-  }
-
   public final class DataSourceKt {
     ctor public DataSourceKt();
   }
@@ -192,18 +173,17 @@
 
   @MainThread public abstract static class PagedList.BoundaryCallback<T> {
     ctor public PagedList.BoundaryCallback();
-    method public void onItemAtEndLoaded(T? itemAtEnd);
-    method public void onItemAtFrontLoaded(T? itemAtFront);
+    method public void onItemAtEndLoaded(T itemAtEnd);
+    method public void onItemAtFrontLoaded(T itemAtFront);
     method public void onZeroItemsLoaded();
   }
 
   public static final class PagedList.Builder<Key, Value> {
     ctor @Deprecated public PagedList.Builder(androidx.paging.DataSource<Key,Value> dataSource, androidx.paging.PagedList.Config config);
     ctor @Deprecated public PagedList.Builder(androidx.paging.DataSource<Key,Value> dataSource, int pageSize);
-    ctor public PagedList.Builder(androidx.paging.PagedSource<Key,Value> pagedSource, androidx.paging.PagedList.Config config);
-    ctor public PagedList.Builder(androidx.paging.PagedSource<Key,Value> pagedSource, int pageSize);
-    method @Deprecated @WorkerThread public androidx.paging.PagedList<Value> build();
-    method public suspend Object buildAsync(kotlin.coroutines.Continuation<? super androidx.paging.PagedList<Value>> p);
+    ctor public PagedList.Builder(androidx.paging.PagedSource<Key,Value> pagedSource, androidx.paging.PagedSource.LoadResult.Page<Key,Value> initialPage, androidx.paging.PagedList.Config config);
+    ctor public PagedList.Builder(androidx.paging.PagedSource<Key,Value> pagedSource, androidx.paging.PagedSource.LoadResult.Page<Key,Value> initialPage, int pageSize);
+    method public androidx.paging.PagedList<Value> build();
     method public androidx.paging.PagedList.Builder<Key,Value> setBoundaryCallback(androidx.paging.PagedList.BoundaryCallback<Value>? boundaryCallback);
     method public androidx.paging.PagedList.Builder<Key,Value> setCoroutineScope(kotlinx.coroutines.CoroutineScope coroutineScope);
     method public androidx.paging.PagedList.Builder<Key,Value> setFetchDispatcher(kotlinx.coroutines.CoroutineDispatcher fetchDispatcher);
@@ -292,24 +272,34 @@
     method public boolean getPlaceholdersEnabled();
   }
 
-  public static final class PagedSource.LoadResult<Key, Value> {
-    ctor public PagedSource.LoadResult(java.util.List<? extends Value> data, @IntRange(from=null) int itemsBefore, @IntRange(from=null) int itemsAfter, Key? nextKey, Key? prevKey);
+  public abstract static sealed class PagedSource.LoadResult<Key, Value> {
+  }
+
+  public static final class PagedSource.LoadResult.Error<Key, Value> extends androidx.paging.PagedSource.LoadResult<Key,Value> {
+    ctor public PagedSource.LoadResult.Error(Throwable throwable);
+    method public Throwable component1();
+    method public androidx.paging.PagedSource.LoadResult.Error<Key,Value> copy(Throwable throwable);
+    method public Throwable getThrowable();
+  }
+
+  public static final class PagedSource.LoadResult.Page<Key, Value> extends androidx.paging.PagedSource.LoadResult<Key,Value> {
+    ctor public PagedSource.LoadResult.Page(java.util.List<? extends Value> data, @IntRange(from=null) int itemsBefore, @IntRange(from=null) int itemsAfter, Key? nextKey, Key? prevKey);
     method public java.util.List<Value> component1();
     method public int component2();
     method public int component3();
     method public Key? component4();
     method public Key? component5();
-    method public androidx.paging.PagedSource.LoadResult<Key,Value> copy(java.util.List<? extends Value> data, int itemsBefore, int itemsAfter, Key? nextKey, Key? prevKey);
+    method public androidx.paging.PagedSource.LoadResult.Page<Key,Value> copy(java.util.List<? extends Value> data, int itemsBefore, int itemsAfter, Key? nextKey, Key? prevKey);
     method public java.util.List<Value> getData();
     method public int getItemsAfter();
     method public int getItemsBefore();
     method public Key? getNextKey();
     method public Key? getPrevKey();
     field public static final int COUNT_UNDEFINED = -1; // 0xffffffff
-    field public static final androidx.paging.PagedSource.LoadResult.Companion! Companion;
+    field public static final androidx.paging.PagedSource.LoadResult.Page.Companion! Companion;
   }
 
-  public static final class PagedSource.LoadResult.Companion {
+  public static final class PagedSource.LoadResult.Page.Companion {
   }
 
   public final class PagedSourceKt {
diff --git a/paging/common/api/public_plus_experimental_3.0.0-alpha01.txt b/paging/common/api/public_plus_experimental_3.0.0-alpha01.txt
index ed2cabf..943ab14 100644
--- a/paging/common/api/public_plus_experimental_3.0.0-alpha01.txt
+++ b/paging/common/api/public_plus_experimental_3.0.0-alpha01.txt
@@ -4,7 +4,6 @@
   public abstract class DataSource<Key, Value> {
     method @AnyThread public void addInvalidatedCallback(androidx.paging.DataSource.InvalidatedCallback onInvalidatedCallback);
     method @AnyThread public final void addInvalidatedCallback(kotlin.jvm.functions.Function0<kotlin.Unit> onInvalidatedCallback);
-    method protected final java.util.concurrent.Executor getExecutor();
     method @AnyThread public void invalidate();
     method @WorkerThread public boolean isInvalid();
     method public abstract suspend Object load$lintWithKotlin(androidx.paging.DataSource.Params<Key> params, kotlin.coroutines.Continuation<? super androidx.paging.DataSource.BaseResult<Value>> p);
@@ -14,20 +13,9 @@
     method public <ToValue> androidx.paging.DataSource<Key,ToValue> mapByPage(kotlin.jvm.functions.Function1<? super java.util.List<? extends Value>,? extends java.util.List<? extends ToValue>> function);
     method @AnyThread public void removeInvalidatedCallback(androidx.paging.DataSource.InvalidatedCallback onInvalidatedCallback);
     method @AnyThread public final void removeInvalidatedCallback(kotlin.jvm.functions.Function0<kotlin.Unit> onInvalidatedCallback);
-    property protected final java.util.concurrent.Executor executor;
     property @WorkerThread public boolean isInvalid;
   }
 
-  public static class DataSource.BaseResult<Value> {
-    method public final boolean getCounted();
-    method public final int getLeadingNulls();
-    method public final Object? getNextKey();
-    method public final int getOffset();
-    method public final Object? getPrevKey();
-    method public final int getTrailingNulls();
-    field public final java.util.List<Value> data;
-  }
-
   public abstract static class DataSource.Factory<Key, Value> {
     ctor public DataSource.Factory();
     method public abstract androidx.paging.DataSource<Key,Value> create();
@@ -41,13 +29,6 @@
     method @AnyThread public void onInvalidated();
   }
 
-  public static final class DataSource.Params<K> {
-    method public int getInitialLoadSize();
-    method public K? getKey();
-    method public int getPageSize();
-    method public boolean getPlaceholdersEnabled();
-  }
-
   public final class DataSourceKt {
     ctor public DataSourceKt();
   }
@@ -192,18 +173,17 @@
 
   @MainThread public abstract static class PagedList.BoundaryCallback<T> {
     ctor public PagedList.BoundaryCallback();
-    method public void onItemAtEndLoaded(T? itemAtEnd);
-    method public void onItemAtFrontLoaded(T? itemAtFront);
+    method public void onItemAtEndLoaded(T itemAtEnd);
+    method public void onItemAtFrontLoaded(T itemAtFront);
     method public void onZeroItemsLoaded();
   }
 
   public static final class PagedList.Builder<Key, Value> {
     ctor @Deprecated public PagedList.Builder(androidx.paging.DataSource<Key,Value> dataSource, androidx.paging.PagedList.Config config);
     ctor @Deprecated public PagedList.Builder(androidx.paging.DataSource<Key,Value> dataSource, int pageSize);
-    ctor public PagedList.Builder(androidx.paging.PagedSource<Key,Value> pagedSource, androidx.paging.PagedList.Config config);
-    ctor public PagedList.Builder(androidx.paging.PagedSource<Key,Value> pagedSource, int pageSize);
-    method @Deprecated @WorkerThread public androidx.paging.PagedList<Value> build();
-    method public suspend Object buildAsync(kotlin.coroutines.Continuation<? super androidx.paging.PagedList<Value>> p);
+    ctor public PagedList.Builder(androidx.paging.PagedSource<Key,Value> pagedSource, androidx.paging.PagedSource.LoadResult.Page<Key,Value> initialPage, androidx.paging.PagedList.Config config);
+    ctor public PagedList.Builder(androidx.paging.PagedSource<Key,Value> pagedSource, androidx.paging.PagedSource.LoadResult.Page<Key,Value> initialPage, int pageSize);
+    method public androidx.paging.PagedList<Value> build();
     method public androidx.paging.PagedList.Builder<Key,Value> setBoundaryCallback(androidx.paging.PagedList.BoundaryCallback<Value>? boundaryCallback);
     method public androidx.paging.PagedList.Builder<Key,Value> setCoroutineScope(kotlinx.coroutines.CoroutineScope coroutineScope);
     method public androidx.paging.PagedList.Builder<Key,Value> setFetchDispatcher(kotlinx.coroutines.CoroutineDispatcher fetchDispatcher);
@@ -292,24 +272,34 @@
     method public boolean getPlaceholdersEnabled();
   }
 
-  public static final class PagedSource.LoadResult<Key, Value> {
-    ctor public PagedSource.LoadResult(java.util.List<? extends Value> data, @IntRange(from=null) int itemsBefore, @IntRange(from=null) int itemsAfter, Key? nextKey, Key? prevKey);
+  public abstract static sealed class PagedSource.LoadResult<Key, Value> {
+  }
+
+  public static final class PagedSource.LoadResult.Error<Key, Value> extends androidx.paging.PagedSource.LoadResult<Key,Value> {
+    ctor public PagedSource.LoadResult.Error(Throwable throwable);
+    method public Throwable component1();
+    method public androidx.paging.PagedSource.LoadResult.Error<Key,Value> copy(Throwable throwable);
+    method public Throwable getThrowable();
+  }
+
+  public static final class PagedSource.LoadResult.Page<Key, Value> extends androidx.paging.PagedSource.LoadResult<Key,Value> {
+    ctor public PagedSource.LoadResult.Page(java.util.List<? extends Value> data, @IntRange(from=null) int itemsBefore, @IntRange(from=null) int itemsAfter, Key? nextKey, Key? prevKey);
     method public java.util.List<Value> component1();
     method public int component2();
     method public int component3();
     method public Key? component4();
     method public Key? component5();
-    method public androidx.paging.PagedSource.LoadResult<Key,Value> copy(java.util.List<? extends Value> data, int itemsBefore, int itemsAfter, Key? nextKey, Key? prevKey);
+    method public androidx.paging.PagedSource.LoadResult.Page<Key,Value> copy(java.util.List<? extends Value> data, int itemsBefore, int itemsAfter, Key? nextKey, Key? prevKey);
     method public java.util.List<Value> getData();
     method public int getItemsAfter();
     method public int getItemsBefore();
     method public Key? getNextKey();
     method public Key? getPrevKey();
     field public static final int COUNT_UNDEFINED = -1; // 0xffffffff
-    field public static final androidx.paging.PagedSource.LoadResult.Companion! Companion;
+    field public static final androidx.paging.PagedSource.LoadResult.Page.Companion! Companion;
   }
 
-  public static final class PagedSource.LoadResult.Companion {
+  public static final class PagedSource.LoadResult.Page.Companion {
   }
 
   public final class PagedSourceKt {
diff --git a/paging/common/api/public_plus_experimental_current.txt b/paging/common/api/public_plus_experimental_current.txt
index ed2cabf..943ab14 100644
--- a/paging/common/api/public_plus_experimental_current.txt
+++ b/paging/common/api/public_plus_experimental_current.txt
@@ -4,7 +4,6 @@
   public abstract class DataSource<Key, Value> {
     method @AnyThread public void addInvalidatedCallback(androidx.paging.DataSource.InvalidatedCallback onInvalidatedCallback);
     method @AnyThread public final void addInvalidatedCallback(kotlin.jvm.functions.Function0<kotlin.Unit> onInvalidatedCallback);
-    method protected final java.util.concurrent.Executor getExecutor();
     method @AnyThread public void invalidate();
     method @WorkerThread public boolean isInvalid();
     method public abstract suspend Object load$lintWithKotlin(androidx.paging.DataSource.Params<Key> params, kotlin.coroutines.Continuation<? super androidx.paging.DataSource.BaseResult<Value>> p);
@@ -14,20 +13,9 @@
     method public <ToValue> androidx.paging.DataSource<Key,ToValue> mapByPage(kotlin.jvm.functions.Function1<? super java.util.List<? extends Value>,? extends java.util.List<? extends ToValue>> function);
     method @AnyThread public void removeInvalidatedCallback(androidx.paging.DataSource.InvalidatedCallback onInvalidatedCallback);
     method @AnyThread public final void removeInvalidatedCallback(kotlin.jvm.functions.Function0<kotlin.Unit> onInvalidatedCallback);
-    property protected final java.util.concurrent.Executor executor;
     property @WorkerThread public boolean isInvalid;
   }
 
-  public static class DataSource.BaseResult<Value> {
-    method public final boolean getCounted();
-    method public final int getLeadingNulls();
-    method public final Object? getNextKey();
-    method public final int getOffset();
-    method public final Object? getPrevKey();
-    method public final int getTrailingNulls();
-    field public final java.util.List<Value> data;
-  }
-
   public abstract static class DataSource.Factory<Key, Value> {
     ctor public DataSource.Factory();
     method public abstract androidx.paging.DataSource<Key,Value> create();
@@ -41,13 +29,6 @@
     method @AnyThread public void onInvalidated();
   }
 
-  public static final class DataSource.Params<K> {
-    method public int getInitialLoadSize();
-    method public K? getKey();
-    method public int getPageSize();
-    method public boolean getPlaceholdersEnabled();
-  }
-
   public final class DataSourceKt {
     ctor public DataSourceKt();
   }
@@ -192,18 +173,17 @@
 
   @MainThread public abstract static class PagedList.BoundaryCallback<T> {
     ctor public PagedList.BoundaryCallback();
-    method public void onItemAtEndLoaded(T? itemAtEnd);
-    method public void onItemAtFrontLoaded(T? itemAtFront);
+    method public void onItemAtEndLoaded(T itemAtEnd);
+    method public void onItemAtFrontLoaded(T itemAtFront);
     method public void onZeroItemsLoaded();
   }
 
   public static final class PagedList.Builder<Key, Value> {
     ctor @Deprecated public PagedList.Builder(androidx.paging.DataSource<Key,Value> dataSource, androidx.paging.PagedList.Config config);
     ctor @Deprecated public PagedList.Builder(androidx.paging.DataSource<Key,Value> dataSource, int pageSize);
-    ctor public PagedList.Builder(androidx.paging.PagedSource<Key,Value> pagedSource, androidx.paging.PagedList.Config config);
-    ctor public PagedList.Builder(androidx.paging.PagedSource<Key,Value> pagedSource, int pageSize);
-    method @Deprecated @WorkerThread public androidx.paging.PagedList<Value> build();
-    method public suspend Object buildAsync(kotlin.coroutines.Continuation<? super androidx.paging.PagedList<Value>> p);
+    ctor public PagedList.Builder(androidx.paging.PagedSource<Key,Value> pagedSource, androidx.paging.PagedSource.LoadResult.Page<Key,Value> initialPage, androidx.paging.PagedList.Config config);
+    ctor public PagedList.Builder(androidx.paging.PagedSource<Key,Value> pagedSource, androidx.paging.PagedSource.LoadResult.Page<Key,Value> initialPage, int pageSize);
+    method public androidx.paging.PagedList<Value> build();
     method public androidx.paging.PagedList.Builder<Key,Value> setBoundaryCallback(androidx.paging.PagedList.BoundaryCallback<Value>? boundaryCallback);
     method public androidx.paging.PagedList.Builder<Key,Value> setCoroutineScope(kotlinx.coroutines.CoroutineScope coroutineScope);
     method public androidx.paging.PagedList.Builder<Key,Value> setFetchDispatcher(kotlinx.coroutines.CoroutineDispatcher fetchDispatcher);
@@ -292,24 +272,34 @@
     method public boolean getPlaceholdersEnabled();
   }
 
-  public static final class PagedSource.LoadResult<Key, Value> {
-    ctor public PagedSource.LoadResult(java.util.List<? extends Value> data, @IntRange(from=null) int itemsBefore, @IntRange(from=null) int itemsAfter, Key? nextKey, Key? prevKey);
+  public abstract static sealed class PagedSource.LoadResult<Key, Value> {
+  }
+
+  public static final class PagedSource.LoadResult.Error<Key, Value> extends androidx.paging.PagedSource.LoadResult<Key,Value> {
+    ctor public PagedSource.LoadResult.Error(Throwable throwable);
+    method public Throwable component1();
+    method public androidx.paging.PagedSource.LoadResult.Error<Key,Value> copy(Throwable throwable);
+    method public Throwable getThrowable();
+  }
+
+  public static final class PagedSource.LoadResult.Page<Key, Value> extends androidx.paging.PagedSource.LoadResult<Key,Value> {
+    ctor public PagedSource.LoadResult.Page(java.util.List<? extends Value> data, @IntRange(from=null) int itemsBefore, @IntRange(from=null) int itemsAfter, Key? nextKey, Key? prevKey);
     method public java.util.List<Value> component1();
     method public int component2();
     method public int component3();
     method public Key? component4();
     method public Key? component5();
-    method public androidx.paging.PagedSource.LoadResult<Key,Value> copy(java.util.List<? extends Value> data, int itemsBefore, int itemsAfter, Key? nextKey, Key? prevKey);
+    method public androidx.paging.PagedSource.LoadResult.Page<Key,Value> copy(java.util.List<? extends Value> data, int itemsBefore, int itemsAfter, Key? nextKey, Key? prevKey);
     method public java.util.List<Value> getData();
     method public int getItemsAfter();
     method public int getItemsBefore();
     method public Key? getNextKey();
     method public Key? getPrevKey();
     field public static final int COUNT_UNDEFINED = -1; // 0xffffffff
-    field public static final androidx.paging.PagedSource.LoadResult.Companion! Companion;
+    field public static final androidx.paging.PagedSource.LoadResult.Page.Companion! Companion;
   }
 
-  public static final class PagedSource.LoadResult.Companion {
+  public static final class PagedSource.LoadResult.Page.Companion {
   }
 
   public final class PagedSourceKt {
diff --git a/paging/common/api/restricted_3.0.0-alpha01.txt b/paging/common/api/restricted_3.0.0-alpha01.txt
index 8df36e0..f47b396 100644
--- a/paging/common/api/restricted_3.0.0-alpha01.txt
+++ b/paging/common/api/restricted_3.0.0-alpha01.txt
@@ -5,8 +5,6 @@
   public abstract class DataSource<Key, Value> {
     method @AnyThread public void addInvalidatedCallback(androidx.paging.DataSource.InvalidatedCallback onInvalidatedCallback);
     method @AnyThread public final void addInvalidatedCallback(kotlin.jvm.functions.Function0<kotlin.Unit> onInvalidatedCallback);
-    method protected final java.util.concurrent.Executor getExecutor();
-    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public final void initExecutor(java.util.concurrent.Executor executor);
     method @AnyThread public void invalidate();
     method @WorkerThread public boolean isInvalid();
     method public abstract suspend Object load$lintWithKotlin(androidx.paging.DataSource.Params<Key> params, kotlin.coroutines.Continuation<? super androidx.paging.DataSource.BaseResult<Value>> p);
@@ -16,20 +14,9 @@
     method public <ToValue> androidx.paging.DataSource<Key,ToValue> mapByPage(kotlin.jvm.functions.Function1<? super java.util.List<? extends Value>,? extends java.util.List<? extends ToValue>> function);
     method @AnyThread public void removeInvalidatedCallback(androidx.paging.DataSource.InvalidatedCallback onInvalidatedCallback);
     method @AnyThread public final void removeInvalidatedCallback(kotlin.jvm.functions.Function0<kotlin.Unit> onInvalidatedCallback);
-    property protected final java.util.concurrent.Executor executor;
     property @WorkerThread public boolean isInvalid;
   }
 
-  public static class DataSource.BaseResult<Value> {
-    method public final boolean getCounted();
-    method public final int getLeadingNulls();
-    method public final Object? getNextKey();
-    method public final int getOffset();
-    method public final Object? getPrevKey();
-    method public final int getTrailingNulls();
-    field public final java.util.List<Value> data;
-  }
-
   public abstract static class DataSource.Factory<Key, Value> {
     ctor public DataSource.Factory();
     method public abstract androidx.paging.DataSource<Key,Value> create();
@@ -43,13 +30,6 @@
     method @AnyThread public void onInvalidated();
   }
 
-  public static final class DataSource.Params<K> {
-    method public int getInitialLoadSize();
-    method public K? getKey();
-    method public int getPageSize();
-    method public boolean getPlaceholdersEnabled();
-  }
-
   public final class DataSourceKt {
     ctor public DataSourceKt();
   }
@@ -181,7 +161,7 @@
     method @Deprecated public final void addWeakCallback(java.util.List<? extends T>? previousSnapshot, androidx.paging.PagedList.Callback callback);
     method public final void addWeakCallback(androidx.paging.PagedList.Callback callback);
     method public final void addWeakLoadStateListener(kotlin.jvm.functions.Function2<? super androidx.paging.LoadType,? super androidx.paging.LoadState,kotlin.Unit> listener);
-    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static final suspend <K, T> Object create(androidx.paging.PagedSource<K,T> p, kotlinx.coroutines.CoroutineScope pagedSource, kotlinx.coroutines.CoroutineDispatcher coroutineScope, kotlinx.coroutines.CoroutineDispatcher notifyDispatcher, kotlinx.coroutines.CoroutineDispatcher fetchDispatcher, androidx.paging.PagedList.BoundaryCallback<T>? initialFetchDispatcher, androidx.paging.PagedList.Config boundaryCallback, K? config, kotlin.coroutines.Continuation<? super androidx.paging.PagedList<T>> key);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static final <K, T> androidx.paging.PagedList<T> create(androidx.paging.PagedSource<K,T> pagedSource, androidx.paging.PagedSource.LoadResult.Page<K,T>? initialPage, kotlinx.coroutines.CoroutineScope coroutineScope, kotlinx.coroutines.CoroutineDispatcher notifyDispatcher, kotlinx.coroutines.CoroutineDispatcher fetchDispatcher, androidx.paging.PagedList.BoundaryCallback<T>? boundaryCallback, androidx.paging.PagedList.Config config, K? key);
     method public abstract void detach();
     method public T? get(int index);
     method public final androidx.paging.PagedList.Config getConfig();
@@ -210,18 +190,17 @@
 
   @MainThread public abstract static class PagedList.BoundaryCallback<T> {
     ctor public PagedList.BoundaryCallback();
-    method public void onItemAtEndLoaded(T? itemAtEnd);
-    method public void onItemAtFrontLoaded(T? itemAtFront);
+    method public void onItemAtEndLoaded(T itemAtEnd);
+    method public void onItemAtFrontLoaded(T itemAtFront);
     method public void onZeroItemsLoaded();
   }
 
   public static final class PagedList.Builder<Key, Value> {
     ctor @Deprecated public PagedList.Builder(androidx.paging.DataSource<Key,Value> dataSource, androidx.paging.PagedList.Config config);
     ctor @Deprecated public PagedList.Builder(androidx.paging.DataSource<Key,Value> dataSource, int pageSize);
-    ctor public PagedList.Builder(androidx.paging.PagedSource<Key,Value> pagedSource, androidx.paging.PagedList.Config config);
-    ctor public PagedList.Builder(androidx.paging.PagedSource<Key,Value> pagedSource, int pageSize);
-    method @Deprecated @WorkerThread public androidx.paging.PagedList<Value> build();
-    method public suspend Object buildAsync(kotlin.coroutines.Continuation<? super androidx.paging.PagedList<Value>> p);
+    ctor public PagedList.Builder(androidx.paging.PagedSource<Key,Value> pagedSource, androidx.paging.PagedSource.LoadResult.Page<Key,Value> initialPage, androidx.paging.PagedList.Config config);
+    ctor public PagedList.Builder(androidx.paging.PagedSource<Key,Value> pagedSource, androidx.paging.PagedSource.LoadResult.Page<Key,Value> initialPage, int pageSize);
+    method public androidx.paging.PagedList<Value> build();
     method public androidx.paging.PagedList.Builder<Key,Value> setBoundaryCallback(androidx.paging.PagedList.BoundaryCallback<Value>? boundaryCallback);
     method public androidx.paging.PagedList.Builder<Key,Value> setCoroutineScope(kotlinx.coroutines.CoroutineScope coroutineScope);
     method public androidx.paging.PagedList.Builder<Key,Value> setFetchDispatcher(kotlinx.coroutines.CoroutineDispatcher fetchDispatcher);
@@ -239,7 +218,7 @@
   }
 
   @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static final class PagedList.Companion {
-    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public suspend <K, T> Object create(androidx.paging.PagedSource<K,T> pagedSource, kotlinx.coroutines.CoroutineScope coroutineScope, kotlinx.coroutines.CoroutineDispatcher notifyDispatcher, kotlinx.coroutines.CoroutineDispatcher fetchDispatcher, kotlinx.coroutines.CoroutineDispatcher initialFetchDispatcher, androidx.paging.PagedList.BoundaryCallback<T>? boundaryCallback, androidx.paging.PagedList.Config config, K? key, kotlin.coroutines.Continuation<? super androidx.paging.PagedList<T>> p);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public <K, T> androidx.paging.PagedList<T> create(androidx.paging.PagedSource<K,T> pagedSource, androidx.paging.PagedSource.LoadResult.Page<K,T>? initialPage, kotlinx.coroutines.CoroutineScope coroutineScope, kotlinx.coroutines.CoroutineDispatcher notifyDispatcher, kotlinx.coroutines.CoroutineDispatcher fetchDispatcher, androidx.paging.PagedList.BoundaryCallback<T>? boundaryCallback, androidx.paging.PagedList.Config config, K? key);
   }
 
   public static final class PagedList.Config {
@@ -329,28 +308,39 @@
     method public boolean getPlaceholdersEnabled();
   }
 
-  public static final class PagedSource.LoadResult<Key, Value> {
-    ctor public PagedSource.LoadResult(java.util.List<? extends Value> data, @IntRange(from=null) int itemsBefore, @IntRange(from=null) int itemsAfter, Key? nextKey, Key? prevKey);
+  public abstract static sealed class PagedSource.LoadResult<Key, Value> {
+  }
+
+  public static final class PagedSource.LoadResult.Error<Key, Value> extends androidx.paging.PagedSource.LoadResult<Key,Value> {
+    ctor public PagedSource.LoadResult.Error(Throwable throwable);
+    method public Throwable component1();
+    method public androidx.paging.PagedSource.LoadResult.Error<Key,Value> copy(Throwable throwable);
+    method public Throwable getThrowable();
+  }
+
+  public static final class PagedSource.LoadResult.Page<Key, Value> extends androidx.paging.PagedSource.LoadResult<Key,Value> {
+    ctor public PagedSource.LoadResult.Page(java.util.List<? extends Value> data, @IntRange(from=null) int itemsBefore, @IntRange(from=null) int itemsAfter, Key? nextKey, Key? prevKey);
     method public java.util.List<Value> component1();
     method public int component2();
     method public int component3();
     method public Key? component4();
     method public Key? component5();
-    method public androidx.paging.PagedSource.LoadResult<Key,Value> copy(java.util.List<? extends Value> data, int itemsBefore, int itemsAfter, Key? nextKey, Key? prevKey);
+    method public androidx.paging.PagedSource.LoadResult.Page<Key,Value> copy(java.util.List<? extends Value> data, int itemsBefore, int itemsAfter, Key? nextKey, Key? prevKey);
     method public java.util.List<Value> getData();
     method public int getItemsAfter();
     method public int getItemsBefore();
     method public Key? getNextKey();
     method public Key? getPrevKey();
     field public static final int COUNT_UNDEFINED = -1; // 0xffffffff
-    field public static final androidx.paging.PagedSource.LoadResult.Companion! Companion;
+    field public static final androidx.paging.PagedSource.LoadResult.Page.Companion! Companion;
   }
 
-  public static final class PagedSource.LoadResult.Companion {
+  public static final class PagedSource.LoadResult.Page.Companion {
   }
 
   public final class PagedSourceKt {
     ctor public PagedSourceKt();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static <Key> androidx.paging.PagedSource.LoadParams<Key> toRefreshLoadParams(androidx.paging.PagedList.Config, Key? key);
   }
 
   @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public final class PagedSourceWrapper<Key, Value> extends androidx.paging.PagedSource<Key,Value> {
diff --git a/paging/common/api/restricted_current.txt b/paging/common/api/restricted_current.txt
index 8df36e0..f47b396 100644
--- a/paging/common/api/restricted_current.txt
+++ b/paging/common/api/restricted_current.txt
@@ -5,8 +5,6 @@
   public abstract class DataSource<Key, Value> {
     method @AnyThread public void addInvalidatedCallback(androidx.paging.DataSource.InvalidatedCallback onInvalidatedCallback);
     method @AnyThread public final void addInvalidatedCallback(kotlin.jvm.functions.Function0<kotlin.Unit> onInvalidatedCallback);
-    method protected final java.util.concurrent.Executor getExecutor();
-    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public final void initExecutor(java.util.concurrent.Executor executor);
     method @AnyThread public void invalidate();
     method @WorkerThread public boolean isInvalid();
     method public abstract suspend Object load$lintWithKotlin(androidx.paging.DataSource.Params<Key> params, kotlin.coroutines.Continuation<? super androidx.paging.DataSource.BaseResult<Value>> p);
@@ -16,20 +14,9 @@
     method public <ToValue> androidx.paging.DataSource<Key,ToValue> mapByPage(kotlin.jvm.functions.Function1<? super java.util.List<? extends Value>,? extends java.util.List<? extends ToValue>> function);
     method @AnyThread public void removeInvalidatedCallback(androidx.paging.DataSource.InvalidatedCallback onInvalidatedCallback);
     method @AnyThread public final void removeInvalidatedCallback(kotlin.jvm.functions.Function0<kotlin.Unit> onInvalidatedCallback);
-    property protected final java.util.concurrent.Executor executor;
     property @WorkerThread public boolean isInvalid;
   }
 
-  public static class DataSource.BaseResult<Value> {
-    method public final boolean getCounted();
-    method public final int getLeadingNulls();
-    method public final Object? getNextKey();
-    method public final int getOffset();
-    method public final Object? getPrevKey();
-    method public final int getTrailingNulls();
-    field public final java.util.List<Value> data;
-  }
-
   public abstract static class DataSource.Factory<Key, Value> {
     ctor public DataSource.Factory();
     method public abstract androidx.paging.DataSource<Key,Value> create();
@@ -43,13 +30,6 @@
     method @AnyThread public void onInvalidated();
   }
 
-  public static final class DataSource.Params<K> {
-    method public int getInitialLoadSize();
-    method public K? getKey();
-    method public int getPageSize();
-    method public boolean getPlaceholdersEnabled();
-  }
-
   public final class DataSourceKt {
     ctor public DataSourceKt();
   }
@@ -181,7 +161,7 @@
     method @Deprecated public final void addWeakCallback(java.util.List<? extends T>? previousSnapshot, androidx.paging.PagedList.Callback callback);
     method public final void addWeakCallback(androidx.paging.PagedList.Callback callback);
     method public final void addWeakLoadStateListener(kotlin.jvm.functions.Function2<? super androidx.paging.LoadType,? super androidx.paging.LoadState,kotlin.Unit> listener);
-    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static final suspend <K, T> Object create(androidx.paging.PagedSource<K,T> p, kotlinx.coroutines.CoroutineScope pagedSource, kotlinx.coroutines.CoroutineDispatcher coroutineScope, kotlinx.coroutines.CoroutineDispatcher notifyDispatcher, kotlinx.coroutines.CoroutineDispatcher fetchDispatcher, androidx.paging.PagedList.BoundaryCallback<T>? initialFetchDispatcher, androidx.paging.PagedList.Config boundaryCallback, K? config, kotlin.coroutines.Continuation<? super androidx.paging.PagedList<T>> key);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static final <K, T> androidx.paging.PagedList<T> create(androidx.paging.PagedSource<K,T> pagedSource, androidx.paging.PagedSource.LoadResult.Page<K,T>? initialPage, kotlinx.coroutines.CoroutineScope coroutineScope, kotlinx.coroutines.CoroutineDispatcher notifyDispatcher, kotlinx.coroutines.CoroutineDispatcher fetchDispatcher, androidx.paging.PagedList.BoundaryCallback<T>? boundaryCallback, androidx.paging.PagedList.Config config, K? key);
     method public abstract void detach();
     method public T? get(int index);
     method public final androidx.paging.PagedList.Config getConfig();
@@ -210,18 +190,17 @@
 
   @MainThread public abstract static class PagedList.BoundaryCallback<T> {
     ctor public PagedList.BoundaryCallback();
-    method public void onItemAtEndLoaded(T? itemAtEnd);
-    method public void onItemAtFrontLoaded(T? itemAtFront);
+    method public void onItemAtEndLoaded(T itemAtEnd);
+    method public void onItemAtFrontLoaded(T itemAtFront);
     method public void onZeroItemsLoaded();
   }
 
   public static final class PagedList.Builder<Key, Value> {
     ctor @Deprecated public PagedList.Builder(androidx.paging.DataSource<Key,Value> dataSource, androidx.paging.PagedList.Config config);
     ctor @Deprecated public PagedList.Builder(androidx.paging.DataSource<Key,Value> dataSource, int pageSize);
-    ctor public PagedList.Builder(androidx.paging.PagedSource<Key,Value> pagedSource, androidx.paging.PagedList.Config config);
-    ctor public PagedList.Builder(androidx.paging.PagedSource<Key,Value> pagedSource, int pageSize);
-    method @Deprecated @WorkerThread public androidx.paging.PagedList<Value> build();
-    method public suspend Object buildAsync(kotlin.coroutines.Continuation<? super androidx.paging.PagedList<Value>> p);
+    ctor public PagedList.Builder(androidx.paging.PagedSource<Key,Value> pagedSource, androidx.paging.PagedSource.LoadResult.Page<Key,Value> initialPage, androidx.paging.PagedList.Config config);
+    ctor public PagedList.Builder(androidx.paging.PagedSource<Key,Value> pagedSource, androidx.paging.PagedSource.LoadResult.Page<Key,Value> initialPage, int pageSize);
+    method public androidx.paging.PagedList<Value> build();
     method public androidx.paging.PagedList.Builder<Key,Value> setBoundaryCallback(androidx.paging.PagedList.BoundaryCallback<Value>? boundaryCallback);
     method public androidx.paging.PagedList.Builder<Key,Value> setCoroutineScope(kotlinx.coroutines.CoroutineScope coroutineScope);
     method public androidx.paging.PagedList.Builder<Key,Value> setFetchDispatcher(kotlinx.coroutines.CoroutineDispatcher fetchDispatcher);
@@ -239,7 +218,7 @@
   }
 
   @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static final class PagedList.Companion {
-    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public suspend <K, T> Object create(androidx.paging.PagedSource<K,T> pagedSource, kotlinx.coroutines.CoroutineScope coroutineScope, kotlinx.coroutines.CoroutineDispatcher notifyDispatcher, kotlinx.coroutines.CoroutineDispatcher fetchDispatcher, kotlinx.coroutines.CoroutineDispatcher initialFetchDispatcher, androidx.paging.PagedList.BoundaryCallback<T>? boundaryCallback, androidx.paging.PagedList.Config config, K? key, kotlin.coroutines.Continuation<? super androidx.paging.PagedList<T>> p);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public <K, T> androidx.paging.PagedList<T> create(androidx.paging.PagedSource<K,T> pagedSource, androidx.paging.PagedSource.LoadResult.Page<K,T>? initialPage, kotlinx.coroutines.CoroutineScope coroutineScope, kotlinx.coroutines.CoroutineDispatcher notifyDispatcher, kotlinx.coroutines.CoroutineDispatcher fetchDispatcher, androidx.paging.PagedList.BoundaryCallback<T>? boundaryCallback, androidx.paging.PagedList.Config config, K? key);
   }
 
   public static final class PagedList.Config {
@@ -329,28 +308,39 @@
     method public boolean getPlaceholdersEnabled();
   }
 
-  public static final class PagedSource.LoadResult<Key, Value> {
-    ctor public PagedSource.LoadResult(java.util.List<? extends Value> data, @IntRange(from=null) int itemsBefore, @IntRange(from=null) int itemsAfter, Key? nextKey, Key? prevKey);
+  public abstract static sealed class PagedSource.LoadResult<Key, Value> {
+  }
+
+  public static final class PagedSource.LoadResult.Error<Key, Value> extends androidx.paging.PagedSource.LoadResult<Key,Value> {
+    ctor public PagedSource.LoadResult.Error(Throwable throwable);
+    method public Throwable component1();
+    method public androidx.paging.PagedSource.LoadResult.Error<Key,Value> copy(Throwable throwable);
+    method public Throwable getThrowable();
+  }
+
+  public static final class PagedSource.LoadResult.Page<Key, Value> extends androidx.paging.PagedSource.LoadResult<Key,Value> {
+    ctor public PagedSource.LoadResult.Page(java.util.List<? extends Value> data, @IntRange(from=null) int itemsBefore, @IntRange(from=null) int itemsAfter, Key? nextKey, Key? prevKey);
     method public java.util.List<Value> component1();
     method public int component2();
     method public int component3();
     method public Key? component4();
     method public Key? component5();
-    method public androidx.paging.PagedSource.LoadResult<Key,Value> copy(java.util.List<? extends Value> data, int itemsBefore, int itemsAfter, Key? nextKey, Key? prevKey);
+    method public androidx.paging.PagedSource.LoadResult.Page<Key,Value> copy(java.util.List<? extends Value> data, int itemsBefore, int itemsAfter, Key? nextKey, Key? prevKey);
     method public java.util.List<Value> getData();
     method public int getItemsAfter();
     method public int getItemsBefore();
     method public Key? getNextKey();
     method public Key? getPrevKey();
     field public static final int COUNT_UNDEFINED = -1; // 0xffffffff
-    field public static final androidx.paging.PagedSource.LoadResult.Companion! Companion;
+    field public static final androidx.paging.PagedSource.LoadResult.Page.Companion! Companion;
   }
 
-  public static final class PagedSource.LoadResult.Companion {
+  public static final class PagedSource.LoadResult.Page.Companion {
   }
 
   public final class PagedSourceKt {
     ctor public PagedSourceKt();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static <Key> androidx.paging.PagedSource.LoadParams<Key> toRefreshLoadParams(androidx.paging.PagedList.Config, Key? key);
   }
 
   @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public final class PagedSourceWrapper<Key, Value> extends androidx.paging.PagedSource<Key,Value> {
diff --git a/paging/common/src/main/kotlin/androidx/paging/ContiguousPagedList.kt b/paging/common/src/main/kotlin/androidx/paging/ContiguousPagedList.kt
index 1ddfd22..6397e13 100644
--- a/paging/common/src/main/kotlin/androidx/paging/ContiguousPagedList.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/ContiguousPagedList.kt
@@ -25,7 +25,7 @@
 import androidx.paging.LoadState.Idle
 import androidx.paging.LoadState.Loading
 import androidx.paging.PagedSource.KeyProvider
-import androidx.paging.PagedSource.LoadResult.Companion.COUNT_UNDEFINED
+import androidx.paging.PagedSource.LoadResult.Page.Companion.COUNT_UNDEFINED
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.launch
@@ -41,7 +41,7 @@
     internal val backgroundDispatcher: CoroutineDispatcher,
     internal val boundaryCallback: BoundaryCallback<V>?,
     config: Config,
-    initialResult: PagedSource.LoadResult<K, V>,
+    initialPage: PagedSource.LoadResult.Page<K, V>,
     lastLoad: Int
 ) : PagedList<V>(
     pagedSource,
@@ -90,7 +90,7 @@
         notifyDispatcher,
         backgroundDispatcher,
         this,
-        initialResult,
+        initialPage,
         storage
     )
 
@@ -113,10 +113,10 @@
      */
     override fun onPageResult(
         type: LoadType,
-        pageResult: PagedSource.LoadResult<*, V>
+        page: PagedSource.LoadResult.Page<*, V>
     ): Boolean {
         var continueLoading = false
-        val page = pageResult.data
+        val list = page.data
 
         // if we end up trimming, we trim from side that's furthest from most recent access
         val trimFromFront = lastLoad > storage.middleOfLoadedRange
@@ -125,7 +125,7 @@
         val skipNewPage = shouldTrim && storage.shouldPreTrimNewPage(
             config.maxSize,
             requiredRemainder,
-            page.size
+            list.size
         )
 
         if (type == END) {
@@ -133,9 +133,9 @@
                 // don't append this data, drop it
                 appendItemsRequested = 0
             } else {
-                storage.appendPage(page, this@ContiguousPagedList)
-                appendItemsRequested -= page.size
-                if (appendItemsRequested > 0 && page.isNotEmpty()) {
+                storage.appendPage(list, this@ContiguousPagedList)
+                appendItemsRequested -= list.size
+                if (appendItemsRequested > 0 && list.isNotEmpty()) {
                     continueLoading = true
                 }
             }
@@ -144,9 +144,9 @@
                 // don't append this data, drop it
                 prependItemsRequested = 0
             } else {
-                storage.prependPage(page, this@ContiguousPagedList)
-                prependItemsRequested -= page.size
-                if (prependItemsRequested > 0 && page.isNotEmpty()) {
+                storage.prependPage(list, this@ContiguousPagedList)
+                prependItemsRequested -= list.size
+                if (prependItemsRequested > 0 && list.isNotEmpty()) {
                     continueLoading = true
                 }
             }
@@ -186,7 +186,7 @@
             }
         }
 
-        triggerBoundaryCallback(type, page)
+        triggerBoundaryCallback(type, list)
         return continueLoading
     }
 
@@ -305,9 +305,9 @@
         if (config.enablePlaceholders) {
             // Placeholders enabled, pass raw data to storage init
             storage.init(
-                if (initialResult.itemsBefore != COUNT_UNDEFINED) initialResult.itemsBefore else 0,
-                initialResult.data,
-                if (initialResult.itemsAfter != COUNT_UNDEFINED) initialResult.itemsAfter else 0,
+                if (initialPage.itemsBefore != COUNT_UNDEFINED) initialPage.itemsBefore else 0,
+                initialPage.data,
+                if (initialPage.itemsAfter != COUNT_UNDEFINED) initialPage.itemsAfter else 0,
                 0,
                 this
             )
@@ -316,9 +316,9 @@
             // may have passed them anyway.
             storage.init(
                 0,
-                initialResult.data,
+                initialPage.data,
                 0,
-                if (initialResult.itemsBefore != COUNT_UNDEFINED) initialResult.itemsBefore else 0,
+                if (initialPage.itemsBefore != COUNT_UNDEFINED) initialPage.itemsBefore else 0,
                 this
             )
         }
@@ -327,10 +327,10 @@
             // Because the ContiguousPagedList wasn't initialized with a last load position,
             // initialize it to the middle of the initial load
             val itemsBefore =
-                if (initialResult.itemsBefore != COUNT_UNDEFINED) initialResult.itemsBefore else 0
-            this.lastLoad = itemsBefore + initialResult.data.size / 2
+                if (initialPage.itemsBefore != COUNT_UNDEFINED) initialPage.itemsBefore else 0
+            this.lastLoad = itemsBefore + initialPage.data.size / 2
         }
-        triggerBoundaryCallback(REFRESH, initialResult.data)
+        triggerBoundaryCallback(REFRESH, initialPage.data)
     }
 
     override fun dispatchCurrentLoadState(callback: LoadStateListener) {
diff --git a/paging/common/src/main/kotlin/androidx/paging/DataSource.kt b/paging/common/src/main/kotlin/androidx/paging/DataSource.kt
index d9a49b1..1ba3a80 100644
--- a/paging/common/src/main/kotlin/androidx/paging/DataSource.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/DataSource.kt
@@ -17,13 +17,11 @@
 package androidx.paging
 
 import androidx.annotation.AnyThread
-import androidx.annotation.RestrictTo
 import androidx.annotation.VisibleForTesting
 import androidx.annotation.WorkerThread
 import androidx.arch.core.util.Function
-import androidx.paging.PagedSource.LoadResult.Companion.COUNT_UNDEFINED
+import androidx.paging.PagedSource.LoadResult.Page.Companion.COUNT_UNDEFINED
 import java.util.concurrent.CopyOnWriteArrayList
-import java.util.concurrent.Executor
 import java.util.concurrent.atomic.AtomicBoolean
 
 typealias  -> Unit
@@ -114,26 +112,6 @@
         @WorkerThread
         get() = _invalid.get()
 
-    private var _executor: Executor? = null
-    /**
-     * `null` until `loadInitial` is called by [PagedList] construction.
-     *
-     * This backing variable is necessary for back-compatibility with paging-common:2.1.0 Java API,
-     * while still providing synthetic accessors for Kotlin API.
-     */
-    protected val executor: Executor
-        get() = _executor ?: throw IllegalStateException(
-            "This DataSource has not been passed to a PagedList, has no executor yet."
-        )
-
-    /**
-     * @hide
-     */
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    fun initExecutor(executor: Executor) {
-        _executor = executor
-    }
-
     /**
      * Factory for DataSources.
      *
@@ -434,7 +412,7 @@
      * @param K Type of the key used to query the [DataSource].
      * @property key Can be `null` for init, otherwise non-null
      */
-    class Params<K : Any> internal constructor(
+    internal class Params<K : Any> internal constructor(
         internal val type: LoadType,
         val key: K?,
         val initialLoadSize: Int,
@@ -446,7 +424,7 @@
      * @param Value Type of the data produced by a [DataSource].
      * @property counted Set to true if the result is an initial load that is passed totalCount
      */
-    open class BaseResult<Value : Any> internal constructor(
+    internal open class BaseResult<Value : Any> internal constructor(
         @JvmField
         val data: List<Value>,
         val prevKey: Any?,
@@ -511,7 +489,7 @@
          * in [PagedSource.LoadResult].
          */
         @Suppress("UNCHECKED_CAST") // Guaranteed to be the correct Key type.
-        internal fun <Key : Any> toLoadResult() = PagedSource.LoadResult(
+        internal fun <Key : Any> toLoadResult() = PagedSource.LoadResult.Page(
             data,
             if (counted) leadingNulls else offset,
             if (counted) trailingNulls else COUNT_UNDEFINED,
diff --git a/paging/common/src/main/kotlin/androidx/paging/InitialPagedList.kt b/paging/common/src/main/kotlin/androidx/paging/InitialPagedList.kt
index f10bf15..32da108 100644
--- a/paging/common/src/main/kotlin/androidx/paging/InitialPagedList.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/InitialPagedList.kt
@@ -41,7 +41,7 @@
     DirectDispatcher,
     null,
     config,
-    PagedSource.LoadResult.empty(),
+    PagedSource.LoadResult.Page.empty(),
     0 // no previous load, so pass 0
 ) {
     override val lastKey = initialKey
diff --git a/paging/common/src/main/kotlin/androidx/paging/PagedList.kt b/paging/common/src/main/kotlin/androidx/paging/PagedList.kt
index afc7038..98d6828 100644
--- a/paging/common/src/main/kotlin/androidx/paging/PagedList.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/PagedList.kt
@@ -19,7 +19,6 @@
 import androidx.annotation.IntRange
 import androidx.annotation.MainThread
 import androidx.annotation.RestrictTo
-import androidx.annotation.WorkerThread
 import androidx.paging.PagedList.Callback
 import androidx.paging.PagedList.Config
 import androidx.paging.PagedList.Config.Builder
@@ -167,7 +166,7 @@
     companion object {
         /**
          * Create a [PagedList] which loads data from the provided data source on a background
-         * thread,posting updates to the main thread.
+         * thread, posting updates to the main thread.
          *
          * @param pagedSource [PagedSource] providing data to the [PagedList]
          * @param notifyDispatcher [CoroutineDispatcher] that will use and consume data from the
@@ -186,12 +185,12 @@
          */
         @JvmStatic
         @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-        suspend fun <K : Any, T : Any> create(
+        fun <K : Any, T : Any> create(
             pagedSource: PagedSource<K, T>,
+            initialPage: PagedSource.LoadResult.Page<K, T>?,
             coroutineScope: CoroutineScope,
             notifyDispatcher: CoroutineDispatcher,
             fetchDispatcher: CoroutineDispatcher,
-            initialFetchDispatcher: CoroutineDispatcher,
             boundaryCallback: BoundaryCallback<T>?,
             config: Config,
             key: K?
@@ -201,18 +200,29 @@
                 else -> ContiguousPagedList.LAST_LOAD_UNSPECIFIED
             }
 
-            val params = PagedSource.LoadParams(
-                LoadType.REFRESH,
-                key,
-                config.initialLoadSizeHint,
-                config.enablePlaceholders,
-                config.pageSize
-            )
-
-            val initialResult = withContext(initialFetchDispatcher) {
-                pagedSource.load(params)
+            val resolvedInitialPage = when (initialPage) {
+                null -> {
+                    // Compatibility codepath - perform the initial load immediately, since caller
+                    // hasn't done it. We block in this case, but it's only used in the legacy path.
+                    val params = PagedSource.LoadParams(
+                        LoadType.REFRESH,
+                        key,
+                        config.initialLoadSizeHint,
+                        config.enablePlaceholders,
+                        config.pageSize
+                    )
+                    runBlocking {
+                        val initialResult = withContext(DirectDispatcher) {
+                            pagedSource.load(params)
+                        }
+                        when (initialResult) {
+                            is PagedSource.LoadResult.Page -> initialResult
+                            is PagedSource.LoadResult.Error -> throw initialResult.throwable
+                        }
+                    }
+                }
+                else -> initialPage
             }
-
             return ContiguousPagedList(
                 pagedSource,
                 coroutineScope,
@@ -220,7 +230,7 @@
                 fetchDispatcher,
                 boundaryCallback,
                 config,
-                initialResult,
+                resolvedInitialPage,
                 lastLoad
             )
         }
@@ -275,6 +285,7 @@
      */
     class Builder<Key : Any, Value : Any> {
         private val pagedSource: PagedSource<Key, Value>
+        private val initialPage: PagedSource.LoadResult.Page<Key, Value>?
         private val config: Config
         private var coroutineScope: CoroutineScope = GlobalScope
         private var notifyDispatcher: CoroutineDispatcher? = null
@@ -291,6 +302,7 @@
         @Deprecated("DataSource is deprecated and has been replaced by PagedSource")
         constructor(dataSource: DataSource<Key, Value>, config: Config) {
             this.pagedSource = PagedSourceWrapper(dataSource)
+            this.initialPage = null
             this.config = config
         }
 
@@ -315,31 +327,49 @@
         )
 
         /**
-         * Create a [PagedList.Builder] with the provided [PagedSource] and [Config].
+         * Create a [PagedList.Builder] with the provided [PagedSource],
+         * initial [PagedSource.LoadResult.Page], and [Config].
          *
          * @param pagedSource [PagedSource] the [PagedList] will load from.
+         * @param initialPage Initial page loaded from the [PagedSource].
          * @param config [Config] that defines how the [PagedList] loads data from its
          * [PagedSource].
          */
-        constructor(pagedSource: PagedSource<Key, Value>, config: Config) {
+        constructor(
+            pagedSource: PagedSource<Key, Value>,
+            initialPage: PagedSource.LoadResult.Page<Key, Value>,
+            config: Config
+        ) {
             this.pagedSource = pagedSource
+            this.initialPage = initialPage
             this.config = config
         }
 
         /**
-         * Create a [PagedList.Builder] with the provided [PagedSource] and page size.
+         * Create a [PagedList.Builder] with the provided [PagedSource],
+         * initial [PagedSource.LoadResult.Page], and page size.
          *
          * This method is a convenience for:
          * ```
-         * PagedList.Builder(pagedSource, PagedList.Config.Builder().setPageSize(pageSize).build())
+         * PagedList.Builder(
+         *     pagedSource,
+         *     page,
+         *     PagedList.Config.Builder().setPageSize(pageSize).build()
+         * )
          * ```
          *
          * @param pagedSource [PagedSource] the [PagedList] will load from.
+         * @param initialPage Initial page loaded from the [PagedSource].
          * @param pageSize [Config] that defines how the [PagedList] loads data from its
          * [PagedSource].
          */
-        constructor(pagedSource: PagedSource<Key, Value>, pageSize: Int) : this(
+        constructor(
+            pagedSource: PagedSource<Key, Value>,
+            initialPage: PagedSource.LoadResult.Page<Key, Value>,
+            pageSize: Int
+        ) : this(
             pagedSource,
+            initialPage,
             PagedList.Config.Builder().setPageSize(pageSize).build()
         )
 
@@ -450,6 +480,8 @@
         /**
          * Creates a [PagedList] with the given parameters.
          *
+         * TODO DOCS
+         *
          * This call will dispatch the [androidx.paging.PagedSource]'s loadInitial method
          * immediately on the current thread, and block the current on the result. This method
          * should always be called on a worker thread to prevent blocking the main thread.
@@ -471,39 +503,13 @@
          *
          * @return The newly constructed [PagedList]
          */
-        @WorkerThread
-        @Deprecated(
-            message = "This method has no means of handling errors encountered during initial " +
-                    "load, and blocks on the initial load result.",
-            replaceWith = ReplaceWith("buildAsync()")
-        )
-        fun build(): PagedList<Value> = runBlocking {
-            create(DirectDispatcher)
-        }
-
-        /**
-         * Creates a [PagedList] asynchronously with the given parameters.
-         *
-         * This call will dispatch [PagedSource.load] immediately with
-         * [LoadType.REFRESH], and return a [PagedList] once it completes, triggering
-         * [loadStateListeners].
-         *
-         * @throws IllegalArgumentException if [notifyDispatcher] or [fetchDispatcher] are not set.
-         *
-         * @return The newly constructed [PagedList]
-         */
-        @Suppress("unused") // Public API
-        suspend fun buildAsync(): PagedList<Value> {
-            return create(fetchDispatcher ?: Dispatchers.IO)
-        }
-
-        private suspend fun create(initialFetchDispatcher: CoroutineDispatcher): PagedList<Value> {
+        fun build(): PagedList<Value> {
             return create(
                 pagedSource,
+                initialPage,
                 coroutineScope,
                 notifyDispatcher ?: Dispatchers.Main.immediate,
                 fetchDispatcher ?: Dispatchers.IO,
-                initialFetchDispatcher,
                 boundaryCallback,
                 config,
                 initialKey
@@ -846,7 +852,7 @@
      * @param T Type loaded by the [PagedList].
      */
     @MainThread
-    abstract class BoundaryCallback<T> {
+    abstract class BoundaryCallback<T : Any> {
         /**
          * Called when zero items are returned from an initial load of the PagedList's data source.
          */
diff --git a/paging/common/src/main/kotlin/androidx/paging/PagedSource.kt b/paging/common/src/main/kotlin/androidx/paging/PagedSource.kt
index abf0046..98ee045 100644
--- a/paging/common/src/main/kotlin/androidx/paging/PagedSource.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/PagedSource.kt
@@ -17,6 +17,7 @@
 package androidx.paging
 
 import androidx.annotation.IntRange
+import androidx.annotation.RestrictTo
 import androidx.paging.PagedSource.KeyProvider
 import androidx.paging.PagedSource.KeyProvider.ItemKey
 import androidx.paging.PagedSource.KeyProvider.PageKey
@@ -36,6 +37,19 @@
 typealias PagedSourceFactory<Key, Value> = () -> PagedSource<Key, Value>
 
 /**
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+fun <Key : Any> PagedList.Config.toRefreshLoadParams(key: Key?): PagedSource.LoadParams<Key> =
+    PagedSource.LoadParams(
+        LoadType.REFRESH,
+        key,
+        initialLoadSizeHint,
+        enablePlaceholders,
+        pageSize
+    )
+
+/**
  * Base class for an abstraction of pageable static data from some source, where loading pages data
  * is typically an expensive operation. Some examples of common [PagedSource]s might be from network
  * or DB.
@@ -124,45 +138,51 @@
         val pageSize: Int
     )
 
-    /**
-     * Result object for a generic load request on a [PagedSource].
-     *
-     * TODO: Builder for Java (also consider @JvmOverloads)
-     */
-    data class LoadResult<Key : Any, Value : Any>(
-        /**
-         * Loaded data
-         */
-        val data: List<Value>,
-        /**
-         * Optional count of items before the loaded data.
-         */
-        @IntRange(from = COUNT_UNDEFINED.toLong())
-        val itemsBefore: Int = COUNT_UNDEFINED,
-        /**
-         * Optional count of items after the loaded data.
-         */
-        @IntRange(from = COUNT_UNDEFINED.toLong())
-        val itemsAfter: Int = COUNT_UNDEFINED,
-        /**
-         * Key for next page - ignored unless you're using [KeyProvider.PageKey]
-         */
-        val nextKey: Key? = null,
-        /**
-         * Key for previous page - ignored unless you're using [KeyProvider.PageKey]
-         */
-        val prevKey: Key? = null
-    ) {
-        internal val counted = itemsBefore != COUNT_UNDEFINED && itemsAfter != COUNT_UNDEFINED
+    sealed class LoadResult<Key : Any, Value : Any> {
+        data class Error<Key : Any, Value : Any>(
+            val throwable: Throwable
+        ) : LoadResult<Key, Value>()
 
-        companion object {
-            const val COUNT_UNDEFINED = -1
+        /**
+         * Success result object for [PagedSource.load]
+         *
+         * TODO: Builder for Java (also consider @JvmOverloads)
+         */
+        data class Page<Key : Any, Value : Any>(
+            /**
+             * Loaded data
+             */
+            val data: List<Value>,
+            /**
+             * Optional count of items before the loaded data.
+             */
+            @IntRange(from = COUNT_UNDEFINED.toLong())
+            val itemsBefore: Int = COUNT_UNDEFINED,
+            /**
+             * Optional count of items after the loaded data.
+             */
+            @IntRange(from = COUNT_UNDEFINED.toLong())
+            val itemsAfter: Int = COUNT_UNDEFINED,
+            /**
+             * Key for next page - ignored unless you're using [KeyProvider.PageKey]
+             */
+            val nextKey: Key? = null,
+            /**
+             * Key for previous page - ignored unless you're using [KeyProvider.PageKey]
+             */
+            val prevKey: Key? = null
+        ) : LoadResult<Key, Value>() {
+            internal val counted = itemsBefore != COUNT_UNDEFINED && itemsAfter != COUNT_UNDEFINED
 
-            @Suppress("MemberVisibilityCanBePrivate") // Prevent synthetic accessor generation.
-            internal val EMPTY = LoadResult(emptyList(), 0, 0, null, null)
+            companion object {
+                const val COUNT_UNDEFINED = -1
 
-            @Suppress("UNCHECKED_CAST") // Can safely ignore, since the list is empty.
-            internal fun <Key : Any, Value : Any> empty() = EMPTY as LoadResult<Key, Value>
+                @Suppress("MemberVisibilityCanBePrivate") // Prevent synthetic accessor generation.
+                internal val EMPTY = Page(emptyList(), 0, 0, null, null)
+
+                @Suppress("UNCHECKED_CAST") // Can safely ignore, since the list is empty.
+                internal fun <Key : Any, Value : Any> empty() = EMPTY as Page<Key, Value>
+            }
         }
     }
 
diff --git a/paging/common/src/main/kotlin/androidx/paging/PagedStorage.kt b/paging/common/src/main/kotlin/androidx/paging/PagedStorage.kt
index 2e6f819..b9e1ee0 100644
--- a/paging/common/src/main/kotlin/androidx/paging/PagedStorage.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/PagedStorage.kt
@@ -299,7 +299,7 @@
         )
     }
 
-    override fun onPageResultResolution(type: LoadType, result: LoadResult<*, T>) {
+    override fun onPageResultResolution(type: LoadType, result: LoadResult.Page<*, T>) {
         // ignored
     }
 
diff --git a/paging/common/src/main/kotlin/androidx/paging/Pager.kt b/paging/common/src/main/kotlin/androidx/paging/Pager.kt
index ab10031..7f8bbac 100644
--- a/paging/common/src/main/kotlin/androidx/paging/Pager.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/Pager.kt
@@ -18,8 +18,7 @@
 
 import androidx.paging.PagedSource.KeyProvider
 import androidx.paging.PagedSource.LoadParams
-import androidx.paging.PagedSource.LoadResult
-import androidx.paging.PagedSource.LoadResult.Companion.COUNT_UNDEFINED
+import androidx.paging.PagedSource.LoadResult.Page.Companion.COUNT_UNDEFINED
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.launch
@@ -32,7 +31,7 @@
     private val notifyDispatcher: CoroutineDispatcher,
     private val fetchDispatcher: CoroutineDispatcher,
     val pageConsumer: PageConsumer<V>,
-    result: LoadResult<K, V>,
+    result: PagedSource.LoadResult.Page<K, V>,
     private val adjacentProvider: AdjacentProvider<V> = SimpleAdjacentProvider()
 ) {
     private val totalCount: Int
@@ -63,28 +62,25 @@
     private fun scheduleLoad(type: LoadType, params: LoadParams<K>) {
         // Listen on the BG thread if the paged source is invalid, since it can be expensive.
         pagedListScope.launch(fetchDispatcher) {
-            try {
-                val value = source.load(params)
+            val value = source.load(params)
 
-                // if invalid, drop result on the floor
-                if (source.invalid) {
-                    detach()
-                    return@launch
-                }
+            // if invalid, drop result on the floor
+            if (source.invalid) {
+                detach()
+                return@launch
+            }
 
-                // Source has been verified to be valid after producing data, so sent data to UI
-                launch(notifyDispatcher) {
-                    onLoadSuccess(type, value)
-                }
-            } catch (throwable: Throwable) {
-                launch(notifyDispatcher) {
-                    onLoadError(type, throwable)
+            // Source has been verified to be valid after producing data, so sent data to UI
+            launch(notifyDispatcher) {
+                when (value) {
+                    is PagedSource.LoadResult.Page -> onLoadSuccess(type, value)
+                    is PagedSource.LoadResult.Error -> onLoadError(type, value.throwable)
                 }
             }
         }
     }
 
-    private fun onLoadSuccess(type: LoadType, value: LoadResult<K, V>) {
+    private fun onLoadSuccess(type: LoadType, value: PagedSource.LoadResult.Page<K, V>) {
         if (isDetached) return // abort!
 
         adjacentProvider.onPageResultResolution(type, value)
@@ -113,7 +109,6 @@
     private fun onLoadError(type: LoadType, throwable: Throwable) {
         if (isDetached) return // abort!
 
-        // TODO: handle nesting
         val state = LoadState.Error(throwable)
         loadStateManager.setState(type, state)
     }
@@ -142,7 +137,7 @@
 
     private fun schedulePrepend() {
         if (!canPrepend()) {
-            onLoadSuccess(LoadType.START, LoadResult.empty())
+            onLoadSuccess(LoadType.START, PagedSource.LoadResult.Page.empty())
             return
         }
 
@@ -169,7 +164,7 @@
 
     private fun scheduleAppend() {
         if (!canAppend()) {
-            onLoadSuccess(LoadType.END, LoadResult.empty())
+            onLoadSuccess(LoadType.END, PagedSource.LoadResult.Page.empty())
             return
         }
 
@@ -210,7 +205,7 @@
         /**
          * @return `true` if we need to fetch more
          */
-        fun onPageResult(type: LoadType, pageResult: LoadResult<*, V>): Boolean
+        fun onPageResult(type: LoadType, page: PagedSource.LoadResult.Page<*, V>): Boolean
 
         fun onStateChanged(type: LoadType, state: LoadState)
     }
@@ -228,7 +223,7 @@
          * implementation of the AdjacentProvider to handle this (generally by ignoring this call if
          * dropping is supported).
          */
-        fun onPageResultResolution(type: LoadType, result: LoadResult<*, V>)
+        fun onPageResultResolution(type: LoadType, result: PagedSource.LoadResult.Page<*, V>)
     }
 
     internal class SimpleAdjacentProvider<V : Any> : AdjacentProvider<V> {
@@ -245,7 +240,10 @@
         private var leadingUnloadedCount: Int = 0
         private var trailingUnloadedCount: Int = 0
 
-        override fun onPageResultResolution(type: LoadType, result: LoadResult<*, V>) {
+        override fun onPageResultResolution(
+            type: LoadType,
+            result: PagedSource.LoadResult.Page<*, V>
+        ) {
             if (result.data.isEmpty()) return
 
             if (type == LoadType.START) {
diff --git a/paging/common/src/main/kotlin/androidx/paging/PositionalDataSource.kt b/paging/common/src/main/kotlin/androidx/paging/PositionalDataSource.kt
index cbe938e..f211bd5 100644
--- a/paging/common/src/main/kotlin/androidx/paging/PositionalDataSource.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/PositionalDataSource.kt
@@ -365,7 +365,7 @@
      *
      * This method is called to load the initial page(s) from the DataSource.
      *
-     * Result list must be a multiple of pageSize to enable efficient tiling.
+     * LoadResult list must be a multiple of pageSize to enable efficient tiling.
      *
      * @param params Parameters for initial load, including requested start position, load size, and
      * page size.
@@ -434,7 +434,7 @@
      *
      * This method is called to load the initial page(s) from the [DataSource].
      *
-     * Result list must be a multiple of pageSize to enable efficient tiling.
+     * LoadResult list must be a multiple of pageSize to enable efficient tiling.
      *
      * @param params Parameters for initial load, including requested start position, load size, and
      * page size.
diff --git a/paging/common/src/test/kotlin/androidx/paging/ContiguousPagedListTest.kt b/paging/common/src/test/kotlin/androidx/paging/ContiguousPagedListTest.kt
index f6b6fdf..6983251 100644
--- a/paging/common/src/test/kotlin/androidx/paging/ContiguousPagedListTest.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/ContiguousPagedListTest.kt
@@ -28,7 +28,6 @@
 import com.nhaarman.mockitokotlin2.verifyNoMoreInteractions
 import com.nhaarman.mockitokotlin2.verifyZeroInteractions
 import kotlinx.coroutines.GlobalScope
-import kotlinx.coroutines.runBlocking
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertSame
 import org.junit.Assert.assertTrue
@@ -36,6 +35,7 @@
 import org.junit.runner.RunWith
 import org.junit.runners.Parameterized
 import kotlin.test.assertFailsWith
+import kotlin.test.assertNotSame
 
 @RunWith(Parameterized::class)
 class ContiguousPagedListTest(private val placeholdersEnabled: Boolean) {
@@ -80,26 +80,26 @@
 
             val result = getClampedRange(start, start + params.loadSize)
             return when {
-                result == null -> throw EXCEPTION
-                placeholdersEnabled -> LoadResult(
+                result == null -> LoadResult.Error(EXCEPTION)
+                placeholdersEnabled -> LoadResult.Page(
                     data = result,
                     itemsBefore = start,
                     itemsAfter = listData.size - result.size - start
                 )
-                else -> LoadResult(result)
+                else -> LoadResult.Page(result)
             }
         }
 
         private fun loadAfter(params: LoadParams<Int>): LoadResult<Int, Item> {
             val result = getClampedRange(params.key!! + 1, params.key!! + 1 + params.loadSize)
-                ?: throw EXCEPTION
-            return LoadResult(result)
+                ?: return LoadResult.Error(EXCEPTION)
+            return LoadResult.Page(result)
         }
 
         private fun loadBefore(params: LoadParams<Int>): LoadResult<Int, Item> {
             val result = getClampedRange(params.key!! - params.loadSize, params.key!!)
-                ?: throw EXCEPTION
-            return LoadResult(result)
+                ?: return LoadResult.Error(EXCEPTION)
+            return LoadResult.Page(result)
         }
 
         private fun getClampedRange(startInc: Int, endExc: Int): List<Item>? {
@@ -119,6 +119,7 @@
 
     private fun <E> MutableList<E>.getAllAndClear(): List<E> {
         val data = this.toList()
+        assertNotSame(data, this)
         this.clear()
         return data
     }
@@ -170,28 +171,22 @@
         boundaryCallback: BoundaryCallback<Item>? = null,
         maxSize: Int = Config.MAX_SIZE_UNBOUNDED,
         pagedSource: PagedSource<Int, Item> = TestPagedSource(listData)
-    ): ContiguousPagedList<Int, Item> {
-        val ret = runBlocking {
-            PagedList.create(
-                pagedSource,
-                GlobalScope,
-                mainThread,
-                backgroundThread,
-                DirectDispatcher,
-                boundaryCallback,
-                Config.Builder()
-                    .setPageSize(pageSize)
-                    .setInitialLoadSizeHint(initLoadSize)
-                    .setPrefetchDistance(prefetchDistance)
-                    .setMaxSize(maxSize)
-                    .setEnablePlaceholders(placeholdersEnabled)
-                    .build(),
-                initialPosition
-            )
-        }
-        @Suppress("UNCHECKED_CAST")
-        return ret as ContiguousPagedList<Int, Item>
-    }
+    ): PagedList<Item> = PagedList.create(
+        pagedSource,
+        null,
+        GlobalScope,
+        mainThread,
+        backgroundThread,
+        boundaryCallback,
+        Config.Builder()
+            .setPageSize(pageSize)
+            .setInitialLoadSizeHint(initLoadSize)
+            .setPrefetchDistance(prefetchDistance)
+            .setMaxSize(maxSize)
+            .setEnablePlaceholders(placeholdersEnabled)
+            .build(),
+        initialPosition
+    )
 
     @Test
     fun construct() {
@@ -206,18 +201,16 @@
         @Suppress("DEPRECATION")
         assertFailsWith<IllegalStateException> { pagedListWithPagedSource.dataSource }
 
-        val pagedListWithDataSource = runBlocking {
-            PagedList.create(
-                PagedSourceWrapper(ItemDataSource()),
-                GlobalScope,
-                FailDispatcher(),
-                DirectDispatcher,
-                DirectDispatcher,
-                null,
-                Config.Builder().setPageSize(10).build(),
-                null
-            )
-        }
+        val pagedListWithDataSource = PagedList.create(
+            PagedSourceWrapper(ItemDataSource()),
+            null,
+            GlobalScope,
+            FailDispatcher(),
+            DirectDispatcher,
+            null,
+            Config.Builder().setPageSize(10).build(),
+            null
+        )
 
         @Suppress("DEPRECATION")
         assertTrue(pagedListWithDataSource.dataSource is ItemDataSource)
diff --git a/paging/common/src/test/kotlin/androidx/paging/ItemKeyedDataSourceTest.kt b/paging/common/src/test/kotlin/androidx/paging/ItemKeyedDataSourceTest.kt
index 36a6381..cd9c21b 100644
--- a/paging/common/src/test/kotlin/androidx/paging/ItemKeyedDataSourceTest.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/ItemKeyedDataSourceTest.kt
@@ -16,7 +16,6 @@
 
 package androidx.paging
 
-import androidx.paging.futures.DirectExecutor
 import androidx.paging.futures.DirectDispatcher
 import com.nhaarman.mockitokotlin2.capture
 import com.nhaarman.mockitokotlin2.mock
@@ -44,7 +43,6 @@
         initialLoadSize: Int,
         enablePlaceholders: Boolean
     ): DataSource.BaseResult<Item> {
-        dataSource.initExecutor(DirectExecutor)
         return dataSource.loadInitial(
             ItemKeyedDataSource.LoadInitialParams(key, initialLoadSize, enablePlaceholders)
         )
@@ -288,20 +286,18 @@
             }
         }
 
-        runBlocking {
-            PagedList.create(
-                PagedSourceWrapper(dataSource),
-                GlobalScope,
-                FailDispatcher(),
-                DirectDispatcher,
-                DirectDispatcher,
-                null,
-                PagedList.Config.Builder()
-                    .setPageSize(10)
-                    .build(),
-                ""
-            )
-        }
+        PagedList.create(
+            PagedSourceWrapper(dataSource),
+            null,
+            GlobalScope,
+            FailDispatcher(),
+            DirectDispatcher,
+            null,
+            PagedList.Config.Builder()
+                .setPageSize(10)
+                .build(),
+            ""
+        )
     }
 
     @Test
diff --git a/paging/common/src/test/kotlin/androidx/paging/PageKeyedDataSourceTest.kt b/paging/common/src/test/kotlin/androidx/paging/PageKeyedDataSourceTest.kt
index 11b7cda..dd397c2 100644
--- a/paging/common/src/test/kotlin/androidx/paging/PageKeyedDataSourceTest.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/PageKeyedDataSourceTest.kt
@@ -20,8 +20,6 @@
 import androidx.testutils.TestDispatcher
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.GlobalScope
-import kotlinx.coroutines.async
-import kotlinx.coroutines.runBlocking
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertFalse
 import org.junit.Assert.assertTrue
@@ -93,21 +91,19 @@
     fun loadFullVerify() {
         // validate paging entire ItemDataSource results in full, correctly ordered data
         val testCoroutineScope = CoroutineScope(EmptyCoroutineContext)
-        val pagedListJob = testCoroutineScope.async(backgroundThread) {
-            PagedList.create(
-                PagedSourceWrapper(ItemDataSource()),
-                testCoroutineScope,
-                mainThread,
-                backgroundThread,
-                backgroundThread,
-                null,
-                PagedList.Config.Builder().setPageSize(100).build(),
-                null
-            )
-        }
+        val pagedList = PagedList.create(
+            PagedSourceWrapper(ItemDataSource()),
+            null,
+            testCoroutineScope,
+            mainThread,
+            DirectDispatcher,
+            null,
+            PagedList.Config.Builder().setPageSize(100).build(),
+            null
+        )
 
-        backgroundThread.executeAll()
-        val pagedList = runBlocking { pagedListJob.await() }
+        // backgroundThread.executeAll()
+        // val pagedList = runBlocking { pagedListJob.await() }
 
         // validate initial load
         assertEquals(PAGE_MAP[INIT_KEY]!!.data, pagedList)
@@ -155,20 +151,18 @@
             }
         }
 
-        runBlocking {
-            PagedList.create(
-                PagedSourceWrapper(dataSource),
-                GlobalScope,
-                FailDispatcher(),
-                DirectDispatcher,
-                DirectDispatcher,
-                null,
-                PagedList.Config.Builder()
-                    .setPageSize(10)
-                    .build(),
-                ""
-            )
-        }
+        PagedList.create(
+            PagedSourceWrapper(dataSource),
+            null,
+            GlobalScope,
+            FailDispatcher(),
+            DirectDispatcher,
+            null,
+            PagedList.Config.Builder()
+                .setPageSize(10)
+                .build(),
+            ""
+        )
     }
 
     @Test
@@ -262,24 +256,18 @@
         val dispatcher = TestDispatcher()
 
         val testCoroutineScope = CoroutineScope(EmptyCoroutineContext)
-        val pagedListJob = testCoroutineScope.async(dispatcher) {
-            PagedList.create(
-                PagedSourceWrapper(dataSource),
-                testCoroutineScope,
-                dispatcher,
-                dispatcher,
-                dispatcher,
-                boundaryCallback,
-                PagedList.Config.Builder()
-                    .setPageSize(10)
-                    .build(),
-                ""
-            )
-        }
-
-        dispatcher.executeAll()
-
-        val pagedList = runBlocking { pagedListJob.await() }
+        val pagedList = PagedList.create(
+            PagedSourceWrapper(dataSource),
+            null,
+            testCoroutineScope,
+            dispatcher,
+            dispatcher,
+            boundaryCallback,
+            PagedList.Config.Builder()
+                .setPageSize(10)
+                .build(),
+            ""
+        )
         pagedList.loadAround(0)
 
         verifyZeroInteractions(boundaryCallback)
@@ -324,23 +312,18 @@
         val dispatcher = TestDispatcher()
 
         val testCoroutineScope = CoroutineScope(EmptyCoroutineContext)
-        val pagedListJob = testCoroutineScope.async(dispatcher) {
-            PagedList.create(
-                PagedSourceWrapper(dataSource),
-                testCoroutineScope,
-                dispatcher,
-                dispatcher,
-                dispatcher,
-                boundaryCallback,
-                PagedList.Config.Builder()
-                    .setPageSize(10)
-                    .build(),
-                ""
-            )
-        }
-        dispatcher.executeAll()
-        val pagedList = runBlocking { pagedListJob.await() }
-
+        val pagedList = PagedList.create(
+            PagedSourceWrapper(dataSource),
+            null,
+            testCoroutineScope,
+            dispatcher,
+            dispatcher,
+            boundaryCallback,
+            PagedList.Config.Builder()
+                .setPageSize(10)
+                .build(),
+            ""
+        )
         pagedList.loadAround(0)
 
         verifyZeroInteractions(boundaryCallback)
diff --git a/paging/common/src/test/kotlin/androidx/paging/PagedListTest.kt b/paging/common/src/test/kotlin/androidx/paging/PagedListTest.kt
index 5415c8c..a8b7600 100644
--- a/paging/common/src/test/kotlin/androidx/paging/PagedListTest.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/PagedListTest.kt
@@ -25,14 +25,13 @@
 import androidx.testutils.TestDispatcher
 import androidx.testutils.TestExecutor
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.async
 import kotlinx.coroutines.runBlocking
 import org.junit.Assert.assertEquals
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
 import kotlin.coroutines.EmptyCoroutineContext
-import kotlin.test.assertFails
+import kotlin.test.assertFailsWith
 
 @RunWith(JUnit4::class)
 class PagedListTest {
@@ -45,7 +44,7 @@
 
             override suspend fun load(params: LoadParams<Int>): LoadResult<Int, String> =
                 when (params.loadType) {
-                    REFRESH -> LoadResult(
+                    REFRESH -> LoadResult.Page(
                         data = listOf("a"),
                         itemsBefore = 0,
                         itemsAfter = 0
@@ -71,78 +70,70 @@
     }
 
     @Test
-    fun createAsync() {
-        val config = Config.Builder()
-            .setPageSize(10)
-            .setEnablePlaceholders(false)
-            .build()
-        var success = false
-
-        val job = testCoroutineScope.async(backgroundThread) {
-            val pagedList = PagedList.create(
-                PagedSourceWrapper(ListDataSource(ITEMS)),
-                testCoroutineScope,
-                mainThread,
-                backgroundThread,
-                backgroundThread,
-                null,
-                config,
-                0
-            )
-
-            assertEquals(ITEMS.subList(0, 30), pagedList)
-            success = true
+    fun createNoInitialPageThrow() {
+        runBlocking {
+            val pagedSource = object : PagedSource<Int, String>() {
+                override val keyProvider = KeyProvider.Positional
+                override suspend fun load(params: LoadParams<Int>): LoadResult<Int, String> {
+                    throw IllegalStateException()
+                }
+            }
+            assertFailsWith<IllegalStateException> {
+                PagedList.create(
+                    pagedSource,
+                    null,
+                    testCoroutineScope,
+                    DirectDispatcher,
+                    DirectDispatcher,
+                    null,
+                    Config(10),
+                    0
+                )
+            }
         }
-
-        backgroundThread.executeAll()
-        runBlocking { job.await() }
-
-        assert(success)
     }
 
     @Test
-    fun createAsyncThrow() {
-        val pagedSource = object : PagedSource<Int, String>() {
-            override val keyProvider = KeyProvider.Positional
-
-            override suspend fun load(params: LoadParams<Int>): LoadResult<Int, String> {
-                throw Exception()
+    fun createNoInitialPageError() {
+        runBlocking {
+            val exception = IllegalStateException()
+            val pagedSource = object : PagedSource<Int, String>() {
+                override val keyProvider = KeyProvider.Positional
+                override suspend fun load(params: LoadParams<Int>): LoadResult<Int, String> {
+                    return LoadResult.Error(exception)
+                }
             }
-        }
 
-        val config = Config.Builder()
-            .setPageSize(10)
-            .setEnablePlaceholders(false)
-            .build()
-        var success = false
-        assertFails {
-            val job = testCoroutineScope.async(backgroundThread) {
+            // create doesn't differentiate between throw vs error runnable, which is why
+            // PagedList.Builder without the initial page is deprecated
+            assertFailsWith<IllegalStateException> {
                 PagedList.create(
                     pagedSource,
-                    testCoroutineScope,
-                    mainThread,
-                    backgroundThread,
-                    backgroundThread,
                     null,
-                    config,
+                    testCoroutineScope,
+                    DirectDispatcher,
+                    DirectDispatcher,
+                    null,
+                    Config(10),
                     0
                 )
-
-                success = true
             }
-
-            backgroundThread.executeAll()
-            runBlocking { job.await() }
         }
-        assert(!success)
     }
 
     @Test
     fun defaults() = runBlocking {
-        val pagedList = Builder(pagedSource, config)
+        val initialPage = pagedSource.load(PagedSource.LoadParams(
+            REFRESH,
+            key = null,
+            loadSize = 10,
+            placeholdersEnabled = false,
+            pageSize = 10
+        )) as PagedSource.LoadResult.Page
+        val pagedList = Builder(pagedSource, initialPage, config)
             .setNotifyDispatcher(DirectDispatcher)
             .setFetchDispatcher(DirectDispatcher)
-            .buildAsync()
+            .build()
 
         assertEquals(pagedSource, pagedList.pagedSource)
         assertEquals(config, pagedList.config)
diff --git a/paging/common/src/test/kotlin/androidx/paging/PagedSourceTest.kt b/paging/common/src/test/kotlin/androidx/paging/PagedSourceTest.kt
index b8bf816..4c9a098 100644
--- a/paging/common/src/test/kotlin/androidx/paging/PagedSourceTest.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/PagedSourceTest.kt
@@ -18,7 +18,7 @@
 
 import androidx.paging.PagedSource.LoadParams
 import androidx.paging.PagedSource.LoadResult
-import androidx.paging.PagedSource.LoadResult.Companion.COUNT_UNDEFINED
+import androidx.paging.PagedSource.LoadResult.Page.Companion.COUNT_UNDEFINED
 import kotlinx.coroutines.runBlocking
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertSame
@@ -56,7 +56,7 @@
         runBlocking {
             val pagedSource = ItemDataSource()
             val key = pagedSource.keyProvider.getKey(ITEMS_BY_NAME_ID[49])
-            val result = loadInitial(pagedSource, key, 10, true)
+            val result = loadInitial(pagedSource, key, 10, true) as LoadResult.Page
 
             assertEquals(45, result.itemsBefore)
             assertEquals(ITEMS_BY_NAME_ID.subList(45, 55), result.data)
@@ -76,8 +76,9 @@
         val pagedSource = ItemDataSource(items = ITEMS_BY_NAME_ID.subList(0, 1))
 
         // this is tricky, since load after and load before with the passed key will fail
-        val result =
-            loadInitial(pagedSource, pagedSource.keyProvider.getKey(ITEMS_BY_NAME_ID[0]), 20, true)
+        val result = loadInitial(
+            pagedSource, pagedSource.keyProvider.getKey(ITEMS_BY_NAME_ID[0]), 20, true
+        ) as LoadResult.Page
 
         assertEquals(0, result.itemsBefore)
         assertEquals(ITEMS_BY_NAME_ID.subList(0, 1), result.data)
@@ -90,7 +91,7 @@
 
         // tricky, because load after key is empty, so another load before and load after required
         val key = pagedSource.keyProvider.getKey(ITEMS_BY_NAME_ID.last())
-        val result = loadInitial(pagedSource, key, 20, true)
+        val result = loadInitial(pagedSource, key, 20, true) as LoadResult.Page
 
         assertEquals(90, result.itemsBefore)
         assertEquals(ITEMS_BY_NAME_ID.subList(90, 100), result.data)
@@ -101,7 +102,7 @@
     fun loadInitial_nullKey() = runBlocking {
         val dataSource = ItemDataSource()
 
-        val result = loadInitial(dataSource, null, 10, true)
+        val result = loadInitial(dataSource, null, 10, true) as LoadResult.Page
 
         assertEquals(0, result.itemsBefore)
         assertEquals(ITEMS_BY_NAME_ID.subList(0, 10), result.data)
@@ -114,7 +115,7 @@
 
         // if key is past entire data set, should return last items in data set
         val key = Key("fz", 0)
-        val result = loadInitial(dataSource, key, 10, true)
+        val result = loadInitial(dataSource, key, 10, true) as LoadResult.Page
 
         // NOTE: ideally we'd load 10 items here, but it adds complexity and unpredictability to
         // do: load after was empty, so pass full size to load before, since this can incur larger
@@ -132,7 +133,7 @@
 
         // dispatchLoadInitial(key, count) == null padding, loadAfter(key, count), null padding
         val key = dataSource.keyProvider.getKey(ITEMS_BY_NAME_ID[49])
-        val result = loadInitial(dataSource, key, 10, false)
+        val result = loadInitial(dataSource, key, 10, false) as LoadResult.Page
 
         assertEquals(COUNT_UNDEFINED, result.itemsBefore)
         assertEquals(ITEMS_BY_NAME_ID.subList(45, 55), result.data)
@@ -145,7 +146,7 @@
 
         // dispatchLoadInitial(key, count) == null padding, loadAfter(key, count), null padding
         val key = dataSource.keyProvider.getKey(ITEMS_BY_NAME_ID[49])
-        val result = loadInitial(dataSource, key, 10, true)
+        val result = loadInitial(dataSource, key, 10, true) as LoadResult.Page
 
         assertEquals(COUNT_UNDEFINED, result.itemsBefore)
         assertEquals(ITEMS_BY_NAME_ID.subList(45, 55), result.data)
@@ -156,7 +157,7 @@
     fun loadInitial_nullKey_uncounted() = runBlocking {
         val dataSource = ItemDataSource(counted = false)
 
-        val result = loadInitial(dataSource, null, 10, true)
+        val result = loadInitial(dataSource, null, 10, true) as LoadResult.Page
 
         assertEquals(COUNT_UNDEFINED, result.itemsBefore)
         assertEquals(ITEMS_BY_NAME_ID.subList(0, 10), result.data)
@@ -171,7 +172,7 @@
 
         // dispatchLoadInitial(key, count) == null padding, loadAfter(key, count), null padding
         val key = dataSource.keyProvider.getKey(ITEMS_BY_NAME_ID[49])
-        val result = loadInitial(dataSource, key, 10, true)
+        val result = loadInitial(dataSource, key, 10, true) as LoadResult.Page
 
         assertEquals(0, result.itemsBefore)
         assertTrue(result.data.isEmpty())
@@ -181,7 +182,7 @@
     @Test
     fun loadInitial_nullKey_empty() = runBlocking {
         val dataSource = ItemDataSource(items = ArrayList())
-        val result = loadInitial(dataSource, null, 10, true)
+        val result = loadInitial(dataSource, null, 10, true) as LoadResult.Page
 
         assertEquals(0, result.itemsBefore)
         assertTrue(result.data.isEmpty())
@@ -197,7 +198,7 @@
         runBlocking {
             val key = dataSource.keyProvider.getKey(ITEMS_BY_NAME_ID[5])
             val params = LoadParams(LoadType.START, key, 5, false, 5)
-            val observed = dataSource.load(params).data
+            val observed = (dataSource.load(params) as LoadResult.Page).data
 
             assertEquals(ITEMS_BY_NAME_ID.subList(0, 5), observed)
 
@@ -217,7 +218,7 @@
         runBlocking {
             val key = dataSource.keyProvider.getKey(ITEMS_BY_NAME_ID[5])
             val params = LoadParams(LoadType.END, key, 5, false, 5)
-            val observed = dataSource.load(params).data
+            val observed = (dataSource.load(params) as LoadResult.Page).data
 
             assertEquals(ITEMS_BY_NAME_ID.subList(6, 11), observed)
 
@@ -286,13 +287,13 @@
 
             return if (params.placeholdersEnabled && counted) {
                 val data = items.subList(start, endExclusive)
-                LoadResult(
+                LoadResult.Page(
                     data = data,
                     itemsBefore = start,
                     itemsAfter = items.size - data.size - start
                 )
             } else {
-                LoadResult(items.subList(start, endExclusive))
+                LoadResult.Page(items.subList(start, endExclusive))
             }
         }
 
@@ -305,7 +306,7 @@
             val start = findFirstIndexAfter(params.key!!)
             val endExclusive = minOf(start + params.loadSize, items.size)
 
-            return LoadResult(items.subList(start, endExclusive))
+            return LoadResult.Page(items.subList(start, endExclusive))
         }
 
         private fun loadBefore(params: LoadParams<Key>): LoadResult<Key, Item> {
@@ -318,7 +319,7 @@
             val endExclusive = maxOf(0, firstIndexBefore + 1)
             val start = maxOf(0, firstIndexBefore - params.loadSize + 1)
 
-            return LoadResult(items.subList(start, endExclusive))
+            return LoadResult.Page(items.subList(start, endExclusive))
         }
 
         private fun findFirstIndexAfter(key: Key): Int {
diff --git a/paging/common/src/test/kotlin/androidx/paging/PagerTest.kt b/paging/common/src/test/kotlin/androidx/paging/PagerTest.kt
index 6de3920..bbfdca2 100644
--- a/paging/common/src/test/kotlin/androidx/paging/PagerTest.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/PagerTest.kt
@@ -34,12 +34,8 @@
     inner class ImmediateListDataSource(private val data: List<String>) :
         PositionalDataSource<String>() {
 
-        init {
-            initExecutor(testExecutor)
-        }
-
         override fun loadInitial(params: LoadInitialParams, callback: LoadInitialCallback<String>) {
-            executor.execute {
+            testExecutor.execute {
                 val totalCount = data.size
 
                 val position = computeInitialLoadPosition(params, totalCount)
@@ -51,7 +47,7 @@
         }
 
         override fun loadRange(params: LoadRangeParams, callback: LoadRangeCallback<String>) {
-            executor.execute {
+            testExecutor.execute {
                 val position = params.startPosition
                 val end = minOf(position + params.loadSize, data.size)
                 callback.onResult(data.subList(position, end))
@@ -85,11 +81,8 @@
             return ret
         }
 
-        override fun onPageResult(
-            type: LoadType,
-            pageResult: LoadResult<*, String>
-        ): Boolean {
-            results.add(Result(type, pageResult))
+        override fun onPageResult(type: LoadType, page: LoadResult.Page<*, String>): Boolean {
+            results.add(Result(type, page))
             return false
         }
 
@@ -104,7 +97,7 @@
         end: Int = 10
     ): Pager<Int, String> {
         val initialData = data.subList(start, end)
-        val initialResult = LoadResult<Int, String>(
+        val initialResult = LoadResult.Page<Int, String>(
             data = initialData,
             itemsBefore = start,
             itemsAfter = data.size - initialData.size - start
@@ -231,7 +224,7 @@
 
         // Pager triggers an immediate empty response here, so we don't need to flush the executor
         assertEquals(
-            listOf(Result(LoadType.END, LoadResult.empty<Int, String>())),
+            listOf(Result(LoadType.END, LoadResult.Page.empty<Int, String>())),
             consumer.takeResults()
         )
         assertEquals(
@@ -249,7 +242,7 @@
 
         // Pager triggers an immediate empty response here, so we don't need to flush the executor
         assertEquals(
-            listOf(Result(LoadType.START, LoadResult.empty<Int, String>())),
+            listOf(Result(LoadType.START, LoadResult.Page.empty<Int, String>())),
             consumer.takeResults()
         )
         assertEquals(
diff --git a/paging/common/src/test/kotlin/androidx/paging/PositionalDataSourceTest.kt b/paging/common/src/test/kotlin/androidx/paging/PositionalDataSourceTest.kt
index 475fc4f..706da3b 100644
--- a/paging/common/src/test/kotlin/androidx/paging/PositionalDataSourceTest.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/PositionalDataSourceTest.kt
@@ -16,7 +16,6 @@
 
 package androidx.paging
 
-import androidx.paging.futures.DirectExecutor
 import kotlinx.coroutines.runBlocking
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertTrue
@@ -177,8 +176,6 @@
             .setEnablePlaceholders(enablePlaceholders)
             .build()
 
-        dataSource.initExecutor(DirectExecutor)
-
         val params = PositionalDataSource.LoadInitialParams(
             0,
             config.initialLoadSizeHint,
diff --git a/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/custom/ItemDataSource.kt b/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/custom/ItemDataSource.kt
index ef6be8c..6671aba 100644
--- a/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/custom/ItemDataSource.kt
+++ b/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/custom/ItemDataSource.kt
@@ -101,16 +101,20 @@
     private fun loadInitial(params: LoadParams<Int>): LoadResult<Int, Item> {
         val position = computeStartPosition(params)
         val loadSize = minOf(COUNT - position, params.loadSize)
-        val data = loadRangeInternal(position, loadSize)
-        return LoadResult(
-            data = data,
-            itemsBefore = position,
-            itemsAfter = COUNT - data.size - position
-        )
+        return try {
+            val data = loadRangeInternal(position, loadSize)
+            LoadResult.Page(
+                data = data,
+                itemsBefore = position,
+                itemsAfter = COUNT - data.size - position
+            )
+        } catch (e: RetryableItemError) {
+            LoadResult.Error(e)
+        }
     }
 
     private fun loadRange(params: LoadParams<Int>): LoadResult<Int, Item> {
         val data = loadRangeInternal(params.key ?: 0, params.loadSize)
-        return LoadResult(data)
+        return LoadResult.Page(data)
     }
 }
diff --git a/paging/runtime/api/api_lint.ignore b/paging/runtime/api/api_lint.ignore
index 24e84aa..117293a 100644
--- a/paging/runtime/api/api_lint.ignore
+++ b/paging/runtime/api/api_lint.ignore
@@ -3,3 +3,7 @@
     Method AsyncPagedListDiffer.getItem appears to be throwing java.lang.IndexOutOfBoundsException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
 DocumentExceptions: androidx.paging.AsyncPagedListDiffer#submitList(androidx.paging.PagedList<T>, Runnable):
     Method AsyncPagedListDiffer.submitList appears to be throwing java.lang.IllegalStateException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
+
+
+SetterReturnsThis: androidx.paging.LivePagedListBuilder:
+    Methods must return the builder object (return type LivePagedListBuilder instead of androidx.paging.LivePagedListBuilder<Key,Value>): method androidx.paging.LivePagedListBuilder.setCoroutineScope(kotlinx.coroutines.CoroutineScope)
diff --git a/paging/runtime/src/androidTest/java/androidx/paging/LivePagedListBuilderTest.kt b/paging/runtime/src/androidTest/java/androidx/paging/LivePagedListBuilderTest.kt
index d5e1e68..d239ec8 100644
--- a/paging/runtime/src/androidTest/java/androidx/paging/LivePagedListBuilderTest.kt
+++ b/paging/runtime/src/androidTest/java/androidx/paging/LivePagedListBuilderTest.kt
@@ -120,11 +120,11 @@
 
                 throwable?.let { error ->
                     throwable = null
-                    throw error
+                    return LoadResult.Error(error)
                 }
 
                 val data = listOf("a", "b")
-                return LoadResult(
+                return LoadResult.Page(
                     data = data,
                     itemsBefore = 0,
                     itemsAfter = 4 - data.size
@@ -132,7 +132,7 @@
             }
 
             private fun loadRange(): LoadResult<Int, String> {
-                return LoadResult(listOf("c", "d"))
+                return LoadResult.Page(listOf("c", "d"))
             }
         }
     }
diff --git a/paging/runtime/src/androidTest/java/androidx/paging/StringPagedList.kt b/paging/runtime/src/androidTest/java/androidx/paging/StringPagedList.kt
index 7de2f6d..5791bb8 100644
--- a/paging/runtime/src/androidTest/java/androidx/paging/StringPagedList.kt
+++ b/paging/runtime/src/androidTest/java/androidx/paging/StringPagedList.kt
@@ -27,12 +27,15 @@
 ) : PagedSource<Any, Value>() {
     override suspend fun load(params: LoadParams<Any>): LoadResult<Any, Value> {
         if (params.loadType == LoadType.REFRESH) {
-            return LoadResult(
+            return LoadResult.Page(
                 data = data,
                 itemsBefore = leadingNulls,
                 itemsAfter = trailingNulls)
         }
-        throw IllegalArgumentException("This test source only supports initial load")
+        // TODO: prevent null-key load start/end
+        return LoadResult.Error(
+            IllegalArgumentException("This test source only supports initial load")
+        )
     }
 }
 
@@ -44,10 +47,10 @@
 ): PagedList<String> = runBlocking {
     PagedList.create(
         pagedSource = FakeSource(leadingNulls, trailingNulls, items.toList()),
+        initialPage = null,
         coroutineScope = GlobalScope,
         notifyDispatcher = DirectDispatcher,
         fetchDispatcher = DirectDispatcher,
-        initialFetchDispatcher = DirectDispatcher,
         boundaryCallback = null,
         config = Config(1, prefetchDistance = 0),
         key = null
diff --git a/paging/runtime/src/main/java/androidx/paging/LivePagedList.kt b/paging/runtime/src/main/java/androidx/paging/LivePagedList.kt
index a79a9af..0c4e4d7 100644
--- a/paging/runtime/src/main/java/androidx/paging/LivePagedList.kt
+++ b/paging/runtime/src/main/java/androidx/paging/LivePagedList.kt
@@ -77,14 +77,30 @@
 
         currentJob?.cancel()
         currentJob = coroutineScope.launch(fetchDispatcher) {
-            try {
-                val pagedList = createPagedList()
-                withContext(notifyDispatcher) {
-                    onSuccess(pagedList)
-                }
-            } catch (throwable: Throwable) {
-                withContext(notifyDispatcher) {
-                    onError(throwable)
+            val pagedSource = pagedSourceFactory()
+            currentData.pagedSource.unregisterInvalidatedCallback(callback)
+            pagedSource.registerInvalidatedCallback(callback)
+
+            withContext(notifyDispatcher) {
+                currentData.setInitialLoadState(REFRESH, Loading)
+            }
+
+            @Suppress("UNCHECKED_CAST")
+            val lastKey = currentData.lastKey as Key?
+            val params = config.toRefreshLoadParams(lastKey)
+            when (val initialResult = pagedSource.load(params)) {
+                is PagedSource.LoadResult.Error -> onError(initialResult.throwable)
+                is PagedSource.LoadResult.Page -> {
+                    onSuccess(PagedList.create(
+                        pagedSource,
+                        initialResult,
+                        coroutineScope,
+                        notifyDispatcher,
+                        fetchDispatcher,
+                        boundaryCallback,
+                        config,
+                        lastKey
+                    ))
                 }
             }
         }
@@ -94,29 +110,6 @@
         previous.setRetryCallback(null)
         next.setRetryCallback(refreshRetryCallback)
     }
-
-    private suspend fun createPagedList(): PagedList<Value> {
-        val pagedSource = pagedSourceFactory()
-        currentData.pagedSource.unregisterInvalidatedCallback(callback)
-        pagedSource.registerInvalidatedCallback(callback)
-
-        withContext(notifyDispatcher) {
-            currentData.setInitialLoadState(REFRESH, Loading)
-        }
-
-        @Suppress("UNCHECKED_CAST") // getLastKey guaranteed to be of 'Key' type
-        val lastKey = currentData.lastKey as Key?
-        return PagedList.create(
-            pagedSource,
-            coroutineScope,
-            notifyDispatcher,
-            fetchDispatcher,
-            fetchDispatcher,
-            boundaryCallback,
-            config,
-            lastKey
-        )
-    }
 }
 
 /**
diff --git a/paging/rxjava2/api/api_lint.ignore b/paging/rxjava2/api/api_lint.ignore
new file mode 100644
index 0000000..9ed9c00
--- /dev/null
+++ b/paging/rxjava2/api/api_lint.ignore
@@ -0,0 +1,3 @@
+// Baseline format: 1.0
+SetterReturnsThis: androidx.paging.RxPagedListBuilder:
+    Methods must return the builder object (return type RxPagedListBuilder instead of androidx.paging.RxPagedListBuilder<Key,Value>): method androidx.paging.RxPagedListBuilder.setInitialLoadKey(Key)
diff --git a/recyclerview/recyclerview-selection/api/1.1.0-alpha07.txt b/recyclerview/recyclerview-selection/api/1.1.0-alpha07.txt
index cea7610..573c952 100644
--- a/recyclerview/recyclerview-selection/api/1.1.0-alpha07.txt
+++ b/recyclerview/recyclerview-selection/api/1.1.0-alpha07.txt
@@ -94,7 +94,7 @@
 
   public abstract class SelectionTracker<K> {
     ctor public SelectionTracker();
-    method public abstract void addObserver(androidx.recyclerview.selection.SelectionTracker.SelectionObserver!);
+    method public abstract void addObserver(androidx.recyclerview.selection.SelectionTracker.SelectionObserver<K!>);
     method public abstract boolean clearSelection();
     method public abstract void copySelection(androidx.recyclerview.selection.MutableSelection<K!>);
     method public abstract boolean deselect(K);
diff --git a/recyclerview/recyclerview-selection/api/current.txt b/recyclerview/recyclerview-selection/api/current.txt
index cea7610..573c952 100644
--- a/recyclerview/recyclerview-selection/api/current.txt
+++ b/recyclerview/recyclerview-selection/api/current.txt
@@ -94,7 +94,7 @@
 
   public abstract class SelectionTracker<K> {
     ctor public SelectionTracker();
-    method public abstract void addObserver(androidx.recyclerview.selection.SelectionTracker.SelectionObserver!);
+    method public abstract void addObserver(androidx.recyclerview.selection.SelectionTracker.SelectionObserver<K!>);
     method public abstract boolean clearSelection();
     method public abstract void copySelection(androidx.recyclerview.selection.MutableSelection<K!>);
     method public abstract boolean deselect(K);
diff --git a/recyclerview/recyclerview-selection/api/public_plus_experimental_1.1.0-alpha07.txt b/recyclerview/recyclerview-selection/api/public_plus_experimental_1.1.0-alpha07.txt
index cea7610..573c952 100644
--- a/recyclerview/recyclerview-selection/api/public_plus_experimental_1.1.0-alpha07.txt
+++ b/recyclerview/recyclerview-selection/api/public_plus_experimental_1.1.0-alpha07.txt
@@ -94,7 +94,7 @@
 
   public abstract class SelectionTracker<K> {
     ctor public SelectionTracker();
-    method public abstract void addObserver(androidx.recyclerview.selection.SelectionTracker.SelectionObserver!);
+    method public abstract void addObserver(androidx.recyclerview.selection.SelectionTracker.SelectionObserver<K!>);
     method public abstract boolean clearSelection();
     method public abstract void copySelection(androidx.recyclerview.selection.MutableSelection<K!>);
     method public abstract boolean deselect(K);
diff --git a/recyclerview/recyclerview-selection/api/public_plus_experimental_current.txt b/recyclerview/recyclerview-selection/api/public_plus_experimental_current.txt
index cea7610..573c952 100644
--- a/recyclerview/recyclerview-selection/api/public_plus_experimental_current.txt
+++ b/recyclerview/recyclerview-selection/api/public_plus_experimental_current.txt
@@ -94,7 +94,7 @@
 
   public abstract class SelectionTracker<K> {
     ctor public SelectionTracker();
-    method public abstract void addObserver(androidx.recyclerview.selection.SelectionTracker.SelectionObserver!);
+    method public abstract void addObserver(androidx.recyclerview.selection.SelectionTracker.SelectionObserver<K!>);
     method public abstract boolean clearSelection();
     method public abstract void copySelection(androidx.recyclerview.selection.MutableSelection<K!>);
     method public abstract boolean deselect(K);
diff --git a/recyclerview/recyclerview-selection/api/restricted_1.1.0-alpha07.txt b/recyclerview/recyclerview-selection/api/restricted_1.1.0-alpha07.txt
index adebbf3..4c24b9b 100644
--- a/recyclerview/recyclerview-selection/api/restricted_1.1.0-alpha07.txt
+++ b/recyclerview/recyclerview-selection/api/restricted_1.1.0-alpha07.txt
@@ -95,7 +95,7 @@
 
   public abstract class SelectionTracker<K> {
     ctor public SelectionTracker();
-    method public abstract void addObserver(androidx.recyclerview.selection.SelectionTracker.SelectionObserver!);
+    method public abstract void addObserver(androidx.recyclerview.selection.SelectionTracker.SelectionObserver<K!>);
     method public abstract boolean clearSelection();
     method public abstract void copySelection(androidx.recyclerview.selection.MutableSelection<K!>);
     method public abstract boolean deselect(K);
diff --git a/recyclerview/recyclerview-selection/api/restricted_current.txt b/recyclerview/recyclerview-selection/api/restricted_current.txt
index adebbf3..4c24b9b 100644
--- a/recyclerview/recyclerview-selection/api/restricted_current.txt
+++ b/recyclerview/recyclerview-selection/api/restricted_current.txt
@@ -95,7 +95,7 @@
 
   public abstract class SelectionTracker<K> {
     ctor public SelectionTracker();
-    method public abstract void addObserver(androidx.recyclerview.selection.SelectionTracker.SelectionObserver!);
+    method public abstract void addObserver(androidx.recyclerview.selection.SelectionTracker.SelectionObserver<K!>);
     method public abstract boolean clearSelection();
     method public abstract void copySelection(androidx.recyclerview.selection.MutableSelection<K!>);
     method public abstract boolean deselect(K);
diff --git a/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/BandSelectionHelperTest.java b/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/BandSelectionHelperTest.java
index 98224d0..d1939cf 100644
--- a/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/BandSelectionHelperTest.java
+++ b/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/BandSelectionHelperTest.java
@@ -58,7 +58,7 @@
     public void setup() throws Exception {
         mItems = TestData.createStringData(10);
         mIsActive = false;
-        mAdapter = new TestAdapter<String>();
+        mAdapter = new TestAdapter<>();
         mAdapter.updateTestModelIds(mItems);
         mBandHost = new TestBandHost();
         mBandPredicate = new TestBandPredicate();
@@ -66,7 +66,7 @@
                 new TestItemKeyProvider<>(ItemKeyProvider.SCOPE_MAPPED, mAdapter);
         OperationMonitor operationMonitor = new OperationMonitor();
 
-        SelectionTracker<String> helper = new DefaultSelectionTracker<String>(
+        SelectionTracker<String> helper = new DefaultSelectionTracker<>(
                 "band-selection-test",
                 keyProvider,
                 SelectionPredicates.createSelectAnything(),
diff --git a/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/DefaultSelectionTrackerTest.java b/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/DefaultSelectionTrackerTest.java
index d9f79d5..9940556 100644
--- a/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/DefaultSelectionTrackerTest.java
+++ b/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/DefaultSelectionTrackerTest.java
@@ -50,8 +50,8 @@
 
     private List<String> mItems;
     private Set<String> mIgnored;
-    private TestAdapter mAdapter;
-    private SelectionPredicate mSelectionPredicate;
+    private TestAdapter<String> mAdapter;
+    private SelectionPredicate<String> mSelectionPredicate;
     private ItemKeyProvider<String> mKeyProvider;
     private DefaultSelectionTracker<String> mTracker;
     private TestSelectionObserver<String> mListener;
@@ -83,7 +83,7 @@
             }
         };
 
-        mKeyProvider = new TestItemKeyProvider<String>(ItemKeyProvider.SCOPE_MAPPED, mAdapter);
+        mKeyProvider = new TestItemKeyProvider<>(ItemKeyProvider.SCOPE_MAPPED, mAdapter);
 
         mTracker = new DefaultSelectionTracker<>(
                 SELECTION_ID,
diff --git a/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/DefaultSelectionTracker_SingleSelectTest.java b/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/DefaultSelectionTracker_SingleSelectTest.java
index f34e952..a5730a2 100644
--- a/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/DefaultSelectionTracker_SingleSelectTest.java
+++ b/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/DefaultSelectionTracker_SingleSelectTest.java
@@ -44,7 +44,7 @@
     public void setUp() throws Exception {
         mItems = TestAdapter.createItemList(100);
         mListener = new TestSelectionObserver<>();
-        TestAdapter adapter = new TestAdapter();
+        TestAdapter<String> adapter = new TestAdapter<>();
         adapter.updateTestModelIds(mItems);
 
         ItemKeyProvider<String> keyProvider =
diff --git a/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/GridModelTest.java b/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/GridModelTest.java
index 7d414c14..7a6e171 100644
--- a/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/GridModelTest.java
+++ b/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/GridModelTest.java
@@ -51,7 +51,7 @@
 
     private GridModel<String> mModel;
     private TestHost mHost;
-    private TestAdapter mAdapter;
+    private TestAdapter<String> mAdapter;
     private Set<String> mLastSelection;
     private int mViewWidth;
 
diff --git a/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/MouseInputHandlerTest.java b/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/MouseInputHandlerTest.java
index eff4173..425bcc6 100644
--- a/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/MouseInputHandlerTest.java
+++ b/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/MouseInputHandlerTest.java
@@ -51,15 +51,15 @@
 @SmallTest
 public final class MouseInputHandlerTest {
 
-    private MouseInputHandler mInputDelegate;
+    private MouseInputHandler<String> mInputDelegate;
 
     private TestOnContextClickListener mMouseCallbacks;
-    private TestOnItemActivatedListener mActivationCallbacks;
-    private TestFocusDelegate mFocusCallbacks;
+    private TestOnItemActivatedListener<String> mActivationCallbacks;
+    private TestFocusDelegate<String> mFocusCallbacks;
 
     private TestItemDetailsLookup mDetailsLookup;
     private SelectionProbe mSelection;
-    private SelectionTracker mSelectionMgr;
+    private SelectionTracker<String> mSelectionMgr;
 
     private TestEvents.Builder mEvent;
 
diff --git a/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/ToolHandlerRegistryTest.java b/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/ToolHandlerRegistryTest.java
index 96bb216..3e2a6a6 100644
--- a/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/ToolHandlerRegistryTest.java
+++ b/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/ToolHandlerRegistryTest.java
@@ -59,11 +59,11 @@
         private static final String UNKNOWN = "unknown";
     }
 
-    private ToolHandlerRegistry mRegistry;
+    private ToolHandlerRegistry<String> mRegistry;
 
     @Before
     public void setUp() {
-        mRegistry = new ToolHandlerRegistry(Handlers.DEFAULT);
+        mRegistry = new ToolHandlerRegistry<>(Handlers.DEFAULT);
     }
 
     @Test
diff --git a/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/TouchInputHandlerTest.java b/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/TouchInputHandlerTest.java
index 15fe7e3..a17e159 100644
--- a/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/TouchInputHandlerTest.java
+++ b/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/TouchInputHandlerTest.java
@@ -48,12 +48,12 @@
 
     private static final List<String> ITEMS = TestData.createStringData(100);
 
-    private TouchInputHandler mInputDelegate;
-    private SelectionTracker mSelectionMgr;
-    private TestSelectionPredicate mSelectionPredicate;
+    private TouchInputHandler<String> mInputDelegate;
+    private SelectionTracker<String> mSelectionMgr;
+    private TestSelectionPredicate<String> mSelectionPredicate;
     private TestRunnable mGestureStarted;
     private TestRunnable mHapticPerformer;
-    private TestOnItemActivatedListener mActivationCallbacks;
+    private TestOnItemActivatedListener<String> mActivationCallbacks;
     private TestItemDetailsLookup mDetailsLookup;
     private SelectionProbe mSelection;
     private TestDragListener mDragInitiatedListener;
@@ -85,7 +85,7 @@
 
     @Test
     public void testTap_ActivatesWhenNoExistingSelection() {
-        ItemDetails doc = mDetailsLookup.initAt(11);
+        ItemDetails<String> doc = mDetailsLookup.initAt(11);
 
         mInputDelegate.onSingleTapUp(TAP);
 
diff --git a/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/testing/SelectionProbe.java b/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/testing/SelectionProbe.java
index 8ac4baf..c424522 100644
--- a/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/testing/SelectionProbe.java
+++ b/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/testing/SelectionProbe.java
@@ -36,7 +36,7 @@
 
     public SelectionProbe(SelectionTracker<String> mgr) {
         mMgr = mgr;
-        mSelectionListener = new TestSelectionObserver<String>();
+        mSelectionListener = new TestSelectionObserver<>();
         mMgr.addObserver(mSelectionListener);
     }
 
diff --git a/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/BandPredicate.java b/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/BandPredicate.java
index 1ee3d73..77fc1598 100644
--- a/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/BandPredicate.java
+++ b/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/BandPredicate.java
@@ -107,14 +107,15 @@
     public static final class NonDraggableArea extends BandPredicate {
 
         private final RecyclerView mRecyclerView;
-        private final ItemDetailsLookup mDetailsLookup;
+        private final ItemDetailsLookup<?> mDetailsLookup;
 
         /**
          * Creates a new instance.
          *
-         * @param recyclerView the owner RecyclerView
+         * @param recyclerView  the owner RecyclerView
          * @param detailsLookup provides access to item details.
          */
+        @SuppressWarnings("rawtypes")
         public NonDraggableArea(
                 @NonNull RecyclerView recyclerView, @NonNull ItemDetailsLookup detailsLookup) {
 
@@ -132,7 +133,8 @@
                 return false;
             }
 
-            @Nullable ItemDetailsLookup.ItemDetails details = mDetailsLookup.getItemDetails(e);
+            @Nullable ItemDetailsLookup.ItemDetails<?> details =
+                    mDetailsLookup.getItemDetails(e);
             return (details == null) || !details.inDragRegion(e);
         }
     }
diff --git a/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/BandSelectionHelper.java b/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/BandSelectionHelper.java
index 495b63f..fa6c31d 100644
--- a/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/BandSelectionHelper.java
+++ b/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/BandSelectionHelper.java
@@ -58,7 +58,7 @@
     static final String TAG = "BandSelectionHelper";
     static final boolean DEBUG = false;
 
-    private final BandHost mHost;
+    private final BandHost<K> mHost;
     private final ItemKeyProvider<K> mKeyProvider;
     @SuppressWarnings("WeakerAccess") /* synthetic access */
     final SelectionTracker<K> mSelectionTracker;
@@ -66,17 +66,17 @@
     private final FocusDelegate<K> mFocusDelegate;
     private final OperationMonitor mLock;
     private final AutoScroller mScroller;
-    private final GridModel.SelectionObserver mGridObserver;
+    private final GridModel.SelectionObserver<K> mGridObserver;
 
     private @Nullable Point mCurrentPosition;
     private @Nullable Point mOrigin;
-    private @Nullable GridModel mModel;
+    private @Nullable GridModel<K> mModel;
 
     /**
      * See {@link BandSelectionHelper#create}.
      */
     BandSelectionHelper(
-            @NonNull BandHost host,
+            @NonNull BandHost<K> host,
             @NonNull AutoScroller scroller,
             @NonNull ItemKeyProvider<K> keyProvider,
             @NonNull SelectionTracker<K> selectionTracker,
@@ -122,7 +122,7 @@
      *
      * @return new BandSelectionHelper instance.
      */
-    static <K> BandSelectionHelper create(
+    static <K> BandSelectionHelper<K> create(
             @NonNull RecyclerView recyclerView,
             @NonNull AutoScroller scroller,
             @DrawableRes int bandOverlayId,
diff --git a/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/DefaultSelectionTracker.java b/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/DefaultSelectionTracker.java
index e8540ce..6534aeb 100644
--- a/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/DefaultSelectionTracker.java
+++ b/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/DefaultSelectionTracker.java
@@ -57,7 +57,7 @@
     private static final String EXTRA_SELECTION_PREFIX = "androidx.recyclerview.selection";
 
     private final Selection<K> mSelection = new Selection<>();
-    private final List<SelectionObserver> mObservers = new ArrayList<>(1);
+    private final List<SelectionObserver<K>> mObservers = new ArrayList<>(1);
     private final ItemKeyProvider<K> mKeyProvider;
     private final SelectionPredicate<K> mSelectionPredicate;
     private final StorageStrategy<K> mStorage;
@@ -79,8 +79,8 @@
      */
     public DefaultSelectionTracker(
             @NonNull String selectionId,
-            @NonNull ItemKeyProvider keyProvider,
-            @NonNull SelectionPredicate selectionPredicate,
+            @NonNull ItemKeyProvider<K> keyProvider,
+            @NonNull SelectionPredicate<K> selectionPredicate,
             @NonNull StorageStrategy<K> storage) {
 
         checkArgument(selectionId != null);
@@ -102,7 +102,7 @@
     }
 
     @Override
-    public void addObserver(@NonNull SelectionObserver callback) {
+    public void addObserver(@NonNull SelectionObserver<K> callback) {
         checkArgument(callback != null);
         mObservers.add(callback);
     }
@@ -113,12 +113,12 @@
     }
 
     @Override
-    public Selection getSelection() {
+    public Selection<K> getSelection() {
         return mSelection;
     }
 
     @Override
-    public void copySelection(@NonNull MutableSelection dest) {
+    public void copySelection(@NonNull MutableSelection<K> dest) {
         dest.copyFrom(mSelection);
     }
 
@@ -128,7 +128,7 @@
     }
 
     @Override
-    protected void restoreSelection(@NonNull Selection other) {
+    protected void restoreSelection(@NonNull Selection<K> other) {
         checkArgument(other != null);
         setItemsSelectedQuietly(other.mSelection, true);
         // NOTE: We intentionally don't restore provisional selection. It's provisional.
@@ -172,7 +172,7 @@
             return;
         }
 
-        Selection prev = clearSelectionQuietly();
+        Selection<K> prev = clearSelectionQuietly();
         notifySelectionCleared(prev);
         notifySelectionChanged();
     }
@@ -182,10 +182,10 @@
      * Returns items in previous selection. Callers are responsible for notifying
      * listeners about changes.
      */
-    private Selection clearSelectionQuietly() {
+    private Selection<K> clearSelectionQuietly() {
         mRange = null;
 
-        MutableSelection prevSelection = new MutableSelection();
+        MutableSelection<K> prevSelection = new MutableSelection();
         if (hasSelection()) {
             copySelection(prevSelection);
             mSelection.clear();
@@ -209,7 +209,7 @@
 
         // Enforce single selection policy.
         if (mSingleSelect && hasSelection()) {
-            Selection prev = clearSelectionQuietly();
+            Selection<K> prev = clearSelectionQuietly();
             notifySelectionCleared(prev);
         }
 
diff --git a/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/EventRouter.java b/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/EventRouter.java
index 2ef4f8a..54d6657 100644
--- a/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/EventRouter.java
+++ b/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/EventRouter.java
@@ -40,7 +40,7 @@
     private final ToolHandlerRegistry<OnItemTouchListener> mDelegates;
 
     EventRouter() {
-        mDelegates = new ToolHandlerRegistry<OnItemTouchListener>(new DummyOnItemTouchListener());
+        mDelegates = new ToolHandlerRegistry<>(new DummyOnItemTouchListener());
     }
 
     /**
diff --git a/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/GridModel.java b/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/GridModel.java
index 993727b3..8453b41 100644
--- a/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/GridModel.java
+++ b/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/GridModel.java
@@ -63,7 +63,7 @@
     private final ItemKeyProvider<K> mKeyProvider;
     private final SelectionPredicate<K> mSelectionPredicate;
 
-    private final List<SelectionObserver> mOnSelectionChangedListeners = new ArrayList<>();
+    private final List<SelectionObserver<K>> mOnSelectionChangedListeners = new ArrayList<>();
 
     // Map from the x-value of the left side of a SparseBooleanArray of adapter positions, keyed
     // by their y-offset. For example, if the first column of the view starts at an x-value of 5,
@@ -103,7 +103,7 @@
 
     @SuppressWarnings("unchecked")
     GridModel(
-            GridHost host,
+            GridHost<K> host,
             ItemKeyProvider<K> keyProvider,
             SelectionPredicate<K> selectionPredicate) {
 
@@ -287,7 +287,7 @@
      */
     @SuppressWarnings("unchecked")
     private void notifySelectionChanged() {
-        for (SelectionObserver listener : mOnSelectionChangedListeners) {
+        for (SelectionObserver<K> listener : mOnSelectionChangedListeners) {
             listener.onSelectionChanged(mSelection);
         }
     }
@@ -403,7 +403,7 @@
         abstract void onSelectionChanged(Set<K> updatedSelection);
     }
 
-    void addOnSelectionChangedListener(SelectionObserver listener) {
+    void addOnSelectionChangedListener(SelectionObserver<K> listener) {
         mOnSelectionChangedListeners.add(listener);
     }
 
diff --git a/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/ItemDetailsLookup.java b/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/ItemDetailsLookup.java
index 9955d7b..3251d19 100644
--- a/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/ItemDetailsLookup.java
+++ b/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/ItemDetailsLookup.java
@@ -241,10 +241,10 @@
         @Override
         public boolean equals(@Nullable Object obj) {
             return (obj instanceof ItemDetails)
-                    && isEqualTo((ItemDetails) obj);
+                    && isEqualTo((ItemDetails<?>) obj);
         }
 
-        private boolean isEqualTo(@NonNull ItemDetails other) {
+        private boolean isEqualTo(@NonNull ItemDetails<?> other) {
             K key = getSelectionKey();
             boolean sameKeys = false;
             if (key == null) {
diff --git a/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/PointerDragEventInterceptor.java b/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/PointerDragEventInterceptor.java
index ea8936f..ef0f077 100644
--- a/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/PointerDragEventInterceptor.java
+++ b/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/PointerDragEventInterceptor.java
@@ -32,12 +32,12 @@
  */
 final class PointerDragEventInterceptor implements OnItemTouchListener {
 
-    private final ItemDetailsLookup mEventDetailsLookup;
+    private final ItemDetailsLookup<?> mEventDetailsLookup;
     private final OnDragInitiatedListener mDragListener;
     private OnItemTouchListener mDelegate;
 
     PointerDragEventInterceptor(
-            ItemDetailsLookup eventDetailsLookup,
+            ItemDetailsLookup<?> eventDetailsLookup,
             OnDragInitiatedListener dragListener,
             @Nullable OnItemTouchListener delegate) {
 
diff --git a/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/Selection.java b/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/Selection.java
index 155789d..6c49015 100644
--- a/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/Selection.java
+++ b/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/Selection.java
@@ -54,9 +54,8 @@
  * (which can be initiated by long pressing an unselected item while there is an
  * existing selection).
  *
- * @see MutableSelection
- *
  * @param <K> Selection key type. @see {@link StorageStrategy} for supported types.
+ * @see MutableSelection
  */
 public class Selection<K> implements Iterable<K> {
 
@@ -78,7 +77,6 @@
     }
 
     /**
-     * @param key
      * @return true if the position is currently selected.
      */
     public boolean contains(@Nullable K key) {
@@ -114,12 +112,13 @@
      * Sets the provisional selection, which is a temporary selection that can be saved,
      * canceled, or adjusted at a later time. When a new provision selection is applied, the old
      * one (if it exists) is abandoned.
+     *
      * @return Map of ids added or removed. Added ids have a value of true, removed are false.
      */
     Map<K, Boolean> setProvisionalSelection(@NonNull Set<K> newSelection) {
         Map<K, Boolean> delta = new LinkedHashMap<>();
 
-        for (K key: mProvisionalSelection) {
+        for (K key : mProvisionalSelection) {
             // Mark each item that used to be in the provisional selection
             // but is not in the new provisional selection.
             if (!newSelection.contains(key) && !mSelection.contains(key)) {
@@ -127,7 +126,7 @@
             }
         }
 
-        for (K key: mSelection) {
+        for (K key : mSelection) {
             // Mark each item that in the selection but is not in the new
             // provisional selection.
             if (!newSelection.contains(key)) {
@@ -135,7 +134,7 @@
             }
         }
 
-        for (K key: newSelection) {
+        for (K key : newSelection) {
             // Mark each item that was not previously in the selection but is in the new
             // provisional selection.
             if (!mSelection.contains(key) && !mProvisionalSelection.contains(key)) {
@@ -146,7 +145,7 @@
         // Now, iterate through the changes and actually add/remove them to/from the current
         // selection. This could not be done in the previous loops because changing the size of
         // the selection mid-iteration changes iteration order erroneously.
-        for (Map.Entry<K, Boolean> entry: delta.entrySet()) {
+        for (Map.Entry<K, Boolean> entry : delta.entrySet()) {
             K key = entry.getKey();
             if (entry.getValue()) {
                 mProvisionalSelection.add(key);
@@ -221,11 +220,11 @@
 
         StringBuilder buffer = new StringBuilder(size() * 28);
         buffer.append("Selection{")
-            .append("primary{size=" + mSelection.size())
-            .append(", entries=" + mSelection)
-            .append("}, provisional{size=" + mProvisionalSelection.size())
-            .append(", entries=" + mProvisionalSelection)
-            .append("}}");
+                .append("primary{size=" + mSelection.size())
+                .append(", entries=" + mSelection)
+                .append("}, provisional{size=" + mProvisionalSelection.size())
+                .append(", entries=" + mProvisionalSelection)
+                .append("}}");
         return buffer.toString();
     }
 
@@ -237,10 +236,10 @@
     @Override
     public boolean equals(Object other) {
         return (this == other)
-                || (other instanceof Selection && isEqualTo((Selection) other));
+                || (other instanceof Selection && isEqualTo((Selection<?>) other));
     }
 
-    private boolean isEqualTo(Selection other) {
+    private boolean isEqualTo(Selection<?> other) {
         return mSelection.equals(other.mSelection)
                 && mProvisionalSelection.equals(other.mProvisionalSelection);
     }
diff --git a/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/SelectionTracker.java b/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/SelectionTracker.java
index 68f9d02..3ca42df 100644
--- a/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/SelectionTracker.java
+++ b/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/SelectionTracker.java
@@ -110,7 +110,7 @@
      * may use an observer to control the enabled status of menu items,
      * or to initiate {@link android.view.ActionMode}.
      */
-    public abstract void addObserver(SelectionObserver observer);
+    public abstract void addObserver(@NonNull SelectionObserver<K> observer);
 
     /** @return true if has a selection */
     public abstract boolean hasSelection();
@@ -703,7 +703,7 @@
 
             // GestureRouter is responsible for routing GestureDetector events
             // to tool-type specific handlers.
-            GestureRouter<MotionInputHandler> gestureRouter = new GestureRouter<>();
+            GestureRouter<MotionInputHandler<K>> gestureRouter = new GestureRouter<>();
             GestureDetector gestureDetector = new GestureDetector(mContext, gestureRouter);
 
             // GestureSelectionHelper provides logic that interprets a combination
@@ -758,7 +758,7 @@
 
             // Provides high level glue for binding touch events
             // and gestures to selection framework.
-            TouchInputHandler<K> touchHandler = new TouchInputHandler<K>(
+            TouchInputHandler<K> touchHandler = new TouchInputHandler<>(
                     tracker,
                     mKeyProvider,
                     mDetailsLookup,
@@ -800,7 +800,7 @@
                 gestureRouter.register(toolType, mouseHandler);
             }
 
-            @Nullable BandSelectionHelper bandHelper = null;
+            @Nullable BandSelectionHelper<K> bandHelper = null;
 
             // Band selection not supported in single select mode, or when key access
             // is limited to anything less than the entire corpus.
diff --git a/recyclerview/recyclerview/api/api_lint.ignore b/recyclerview/recyclerview/api/api_lint.ignore
index 4579639..b7865b2 100644
--- a/recyclerview/recyclerview/api/api_lint.ignore
+++ b/recyclerview/recyclerview/api/api_lint.ignore
@@ -759,3 +759,7 @@
     Missing nullability on parameter `source` in method `LayoutParams`
 MissingNullability: androidx.recyclerview.widget.StaggeredGridLayoutManager.LayoutParams#LayoutParams(androidx.recyclerview.widget.RecyclerView.LayoutParams) parameter #0:
     Missing nullability on parameter `source` in method `LayoutParams`
+
+
+SetterReturnsThis: androidx.recyclerview.widget.AsyncDifferConfig.Builder:
+    Methods must return the builder object (return type Builder instead of androidx.recyclerview.widget.AsyncDifferConfig.Builder<T>): method androidx.recyclerview.widget.AsyncDifferConfig.Builder.setBackgroundThreadExecutor(java.util.concurrent.Executor)
diff --git a/room/runtime/api/api_lint.ignore b/room/runtime/api/api_lint.ignore
index cfb48c3..e00e9db 100644
--- a/room/runtime/api/api_lint.ignore
+++ b/room/runtime/api/api_lint.ignore
@@ -17,3 +17,7 @@
 
 RegistrationName: androidx.room.RoomDatabase.Builder#addCallback(androidx.room.RoomDatabase.Callback):
     Callback methods should be named register/unregister; was addCallback
+
+
+SetterReturnsThis: androidx.room.RoomDatabase.Builder:
+    Methods must return the builder object (return type Builder instead of androidx.room.RoomDatabase.Builder<T>): method androidx.room.RoomDatabase.Builder.setJournalMode(androidx.room.RoomDatabase.JournalMode)
diff --git a/samples/Support7Demos/src/main/java/com/example/android/supportv7/widget/selection/fancy/FancySelectionDemoActivity.java b/samples/Support7Demos/src/main/java/com/example/android/supportv7/widget/selection/fancy/FancySelectionDemoActivity.java
index 4e46aa7..8d2c2e2 100644
--- a/samples/Support7Demos/src/main/java/com/example/android/supportv7/widget/selection/fancy/FancySelectionDemoActivity.java
+++ b/samples/Support7Demos/src/main/java/com/example/android/supportv7/widget/selection/fancy/FancySelectionDemoActivity.java
@@ -90,7 +90,7 @@
 
         // TODO: Glue selection to ActionMode, since that'll be a common practice.
         mSelectionTracker.addObserver(
-                new SelectionObserver<Long>() {
+                new SelectionObserver<Uri>() {
                     @Override
                     public void onSelectionChanged() {
                         Log.i(TAG, "Selection changed to: " + mSelectionTracker.getSelection());
diff --git a/testutils/testutils-ktx/src/main/java/androidx/testutils/TestDispatcher.kt b/testutils/testutils-ktx/src/main/java/androidx/testutils/TestDispatcher.kt
index 127eaef..be8b54f 100644
--- a/testutils/testutils-ktx/src/main/java/androidx/testutils/TestDispatcher.kt
+++ b/testutils/testutils-ktx/src/main/java/androidx/testutils/TestDispatcher.kt
@@ -21,7 +21,7 @@
 import kotlin.coroutines.CoroutineContext
 
 /**
- * [CoroutineDispatcher] which keeps track of all its queues jobs.
+ * [CoroutineDispatcher] which keeps track of all its queued jobs.
  */
 class TestDispatcher : CoroutineDispatcher() {
     val queue = ConcurrentLinkedQueue<Runnable>()
@@ -31,8 +31,9 @@
     }
 
     fun executeAll() {
-        while (queue.peek() != null) {
-            queue.poll()!!.run()
-        }
+        do {
+            val runnable = queue.poll()
+            runnable?.run()
+        } while (runnable != null)
     }
 }
diff --git a/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/SimpleRadioButtonBenchmark.kt b/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/SimpleRadioButtonBenchmark.kt
new file mode 100644
index 0000000..347f2b2
--- /dev/null
+++ b/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/SimpleRadioButtonBenchmark.kt
@@ -0,0 +1,199 @@
+/*
+ * Copyright 2019 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.ui.benchmark.test
+
+import android.app.Activity
+import androidx.benchmark.junit4.BenchmarkRule
+import androidx.test.filters.LargeTest
+import androidx.test.rule.ActivityTestRule
+import androidx.ui.benchmark.measureDrawPerf
+import androidx.ui.benchmark.measureFirstCompose
+import androidx.ui.benchmark.measureFirstDraw
+import androidx.ui.benchmark.measureFirstLayout
+import androidx.ui.benchmark.measureFirstMeasure
+import androidx.ui.benchmark.measureLayoutPerf
+import androidx.ui.benchmark.toggleStateMeasureDraw
+import androidx.ui.benchmark.toggleStateMeasureLayout
+import androidx.ui.benchmark.toggleStateMeasureMeasure
+import androidx.ui.benchmark.toggleStateMeasureRecompose
+import androidx.ui.test.cases.SimpleRadioButton1TestCase
+import androidx.ui.test.cases.SimpleRadioButton2TestCase
+import androidx.ui.test.cases.SimpleRadioButton3TestCase
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@LargeTest
+@RunWith(JUnit4::class)
+class SimpleRadioButtonBenchmark {
+    @get:Rule
+    val benchmarkRule = BenchmarkRule()
+
+    @get:Rule
+    val activityRule = ActivityTestRule(Activity::class.java)
+
+    private val activity: Activity get() = activityRule.activity
+
+    @Test
+    fun radio_button_1_first_compose() {
+        benchmarkRule.measureFirstCompose(activity, SimpleRadioButton1TestCase(activity))
+    }
+
+    @Test
+    fun radio_button_1_first_measure() {
+        benchmarkRule.measureFirstMeasure(activity, SimpleRadioButton1TestCase(activity))
+    }
+
+    @Test
+    fun radio_button_1_first_layout() {
+        benchmarkRule.measureFirstLayout(activity, SimpleRadioButton1TestCase(activity))
+    }
+
+    @Test
+    fun radio_button_1_first_draw() {
+        benchmarkRule.measureFirstDraw(activity, SimpleRadioButton1TestCase(activity))
+    }
+
+    @Test
+    fun radio_button_1_update_recompose() {
+        benchmarkRule.toggleStateMeasureRecompose(activity, SimpleRadioButton1TestCase(activity))
+    }
+
+    @Test
+    fun radio_button_1_update_measure() {
+        benchmarkRule.toggleStateMeasureMeasure(activity, SimpleRadioButton1TestCase(activity))
+    }
+
+    @Test
+    fun radio_button_1_update_layout() {
+        benchmarkRule.toggleStateMeasureLayout(activity, SimpleRadioButton1TestCase(activity))
+    }
+
+    @Test
+    fun radio_button_1_update_draw() {
+        benchmarkRule.toggleStateMeasureDraw(activity, SimpleRadioButton1TestCase(activity))
+    }
+
+    @Test
+    fun radio_button_1_layout() {
+        benchmarkRule.measureLayoutPerf(activity, SimpleRadioButton1TestCase(activity))
+    }
+
+    @Test
+    fun radio_button_1_draw() {
+        benchmarkRule.measureDrawPerf(activity, SimpleRadioButton1TestCase(activity))
+    }
+
+    @Test
+    fun radio_button_2_first_compose() {
+        benchmarkRule.measureFirstCompose(activity, SimpleRadioButton2TestCase(activity))
+    }
+
+    @Test
+    fun radio_button_2_first_measure() {
+        benchmarkRule.measureFirstMeasure(activity, SimpleRadioButton2TestCase(activity))
+    }
+
+    @Test
+    fun radio_button_2_first_layout() {
+        benchmarkRule.measureFirstLayout(activity, SimpleRadioButton2TestCase(activity))
+    }
+
+    @Test
+    fun radio_button_2_first_draw() {
+        benchmarkRule.measureFirstDraw(activity, SimpleRadioButton2TestCase(activity))
+    }
+
+    @Test
+    fun radio_button_2_update_recompose() {
+        benchmarkRule.toggleStateMeasureRecompose(activity, SimpleRadioButton2TestCase(activity))
+    }
+
+    @Test
+    fun radio_button_2_update_measure() {
+        benchmarkRule.toggleStateMeasureMeasure(activity, SimpleRadioButton2TestCase(activity))
+    }
+
+    @Test
+    fun radio_button_2_update_layout() {
+        benchmarkRule.toggleStateMeasureLayout(activity, SimpleRadioButton2TestCase(activity))
+    }
+
+    @Test
+    fun radio_button_2_update_draw() {
+        benchmarkRule.toggleStateMeasureDraw(activity, SimpleRadioButton2TestCase(activity))
+    }
+
+    @Test
+    fun radio_button_2_layout() {
+        benchmarkRule.measureLayoutPerf(activity, SimpleRadioButton2TestCase(activity))
+    }
+
+    @Test
+    fun radio_button_2_draw() {
+        benchmarkRule.measureDrawPerf(activity, SimpleRadioButton2TestCase(activity))
+    }
+
+    @Test
+    fun radio_button_3_first_compose() {
+        benchmarkRule.measureFirstCompose(activity, SimpleRadioButton3TestCase(activity))
+    }
+
+    @Test
+    fun radio_button_3_first_measure() {
+        benchmarkRule.measureFirstMeasure(activity, SimpleRadioButton3TestCase(activity))
+    }
+
+    @Test
+    fun radio_button_3_first_layout() {
+        benchmarkRule.measureFirstLayout(activity, SimpleRadioButton3TestCase(activity))
+    }
+
+    @Test
+    fun radio_button_3_first_draw() {
+        benchmarkRule.measureFirstDraw(activity, SimpleRadioButton3TestCase(activity))
+    }
+
+    @Test
+    fun radio_button_3_update_measure() {
+        benchmarkRule.toggleStateMeasureMeasure(activity, SimpleRadioButton3TestCase(activity),
+            toggleCausesRecompose = false)
+    }
+
+    @Test
+    fun radio_button_3_update_layout() {
+        benchmarkRule.toggleStateMeasureLayout(activity, SimpleRadioButton3TestCase(activity),
+            toggleCausesRecompose = false)
+    }
+
+    @Test
+    fun radio_button_3_update_draw() {
+        benchmarkRule.toggleStateMeasureDraw(activity, SimpleRadioButton3TestCase(activity),
+            toggleCausesRecompose = false)
+    }
+
+    @Test
+    fun radio_button_3_layout() {
+        benchmarkRule.measureLayoutPerf(activity, SimpleRadioButton3TestCase(activity))
+    }
+
+    @Test
+    fun radio_button_3_draw() {
+        benchmarkRule.measureDrawPerf(activity, SimpleRadioButton3TestCase(activity))
+    }
+}
diff --git a/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/core/TextBasicBenchmark.kt b/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/core/TextBasicBenchmark.kt
index a8cf1be..c788ed7 100644
--- a/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/core/TextBasicBenchmark.kt
+++ b/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/core/TextBasicBenchmark.kt
@@ -19,7 +19,6 @@
 import android.app.Activity
 import androidx.benchmark.junit4.BenchmarkRule
 import androidx.test.filters.LargeTest
-import androidx.test.filters.Suppress
 import androidx.test.rule.ActivityTestRule
 import androidx.ui.benchmark.measureDrawPerf
 import androidx.ui.benchmark.measureFirstCompose
@@ -28,13 +27,12 @@
 import androidx.ui.benchmark.measureFirstMeasure
 import androidx.ui.benchmark.measureLayoutPerf
 import androidx.ui.test.DisableTransitions
-import androidx.ui.test.RandomTextGeneratorTestRule
+import androidx.ui.test.TextBenchmarkTestRule
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.Parameterized
 
-@Suppress
 @LargeTest
 @RunWith(Parameterized::class)
 class TextBasicBenchmark(
@@ -50,19 +48,19 @@
     val disableAnimationRule = DisableTransitions()
 
     @get:Rule
-    val textGeneratorRule = RandomTextGeneratorTestRule()
+    val textBenchmarkRule = TextBenchmarkTestRule()
 
     private val activity: Activity get() = activityRule.activity
 
     companion object {
         @JvmStatic
         @Parameterized.Parameters(name = "length={0}")
-        fun initParameters(): Array<Any> = arrayOf(8, 16, 32, 64, 128, 256, 512, 1024)
+        fun initParameters(): Array<Any> = arrayOf(32, 512)
     }
 
     @Test
     fun first_compose() {
-        textGeneratorRule.generator { textGenerator ->
+        textBenchmarkRule.generator { textGenerator ->
             benchmarkRule.measureFirstCompose(
                 activity,
                 TextBasicTestCase(activity, textLength, textGenerator)
@@ -72,7 +70,7 @@
 
     @Test
     fun first_measure() {
-        textGeneratorRule.generator { textGenerator ->
+        textBenchmarkRule.generator { textGenerator ->
             benchmarkRule.measureFirstMeasure(
                 activity,
                 TextBasicTestCase(activity, textLength, textGenerator)
@@ -82,7 +80,7 @@
 
     @Test
     fun first_layout() {
-        textGeneratorRule.generator { textGenerator ->
+        textBenchmarkRule.generator { textGenerator ->
             benchmarkRule.measureFirstLayout(
                 activity,
                 TextBasicTestCase(activity, textLength, textGenerator)
@@ -92,7 +90,7 @@
 
     @Test
     fun first_draw() {
-        textGeneratorRule.generator { textGenerator ->
+        textBenchmarkRule.generator { textGenerator ->
             benchmarkRule.measureFirstDraw(
                 activity,
                 TextBasicTestCase(activity, textLength, textGenerator)
@@ -102,7 +100,7 @@
 
     @Test
     fun layout() {
-        textGeneratorRule.generator { textGenerator ->
+        textBenchmarkRule.generator { textGenerator ->
             benchmarkRule.measureLayoutPerf(
                 activity,
                 TextBasicTestCase(activity, textLength, textGenerator)
@@ -112,7 +110,7 @@
 
     @Test
     fun draw() {
-        textGeneratorRule.generator { textGenerator ->
+        textBenchmarkRule.generator { textGenerator ->
             benchmarkRule.measureDrawPerf(
                 activity,
                 TextBasicTestCase(activity, textLength, textGenerator)
diff --git a/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/core/TextMultiStyleBenchmark.kt b/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/core/TextMultiStyleBenchmark.kt
index fd08fe9..19e5813 100644
--- a/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/core/TextMultiStyleBenchmark.kt
+++ b/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/core/TextMultiStyleBenchmark.kt
@@ -28,7 +28,7 @@
 import androidx.ui.benchmark.measureFirstMeasure
 import androidx.ui.benchmark.measureLayoutPerf
 import androidx.ui.test.DisableTransitions
-import androidx.ui.test.RandomTextGeneratorTestRule
+import androidx.ui.test.TextBenchmarkTestRule
 import androidx.ui.test.cartesian
 import org.junit.Rule
 import org.junit.Test
@@ -40,8 +40,7 @@
 @RunWith(Parameterized::class)
 class TextMultiStyleBenchmark(
     private val textLength: Int,
-    private val styleCount: Int,
-    private val hasMetricAffectingStyle: Boolean
+    private val styleCount: Int
 ) {
     @get:Rule
     val benchmarkRule = BenchmarkRule()
@@ -53,30 +52,28 @@
     val disableAnimationRule = DisableTransitions()
 
     @get:Rule
-    val textGeneratorRule = RandomTextGeneratorTestRule()
+    val textBenchmarkRule = TextBenchmarkTestRule()
 
     private val activity: Activity get() = activityRule.activity
 
     companion object {
         @JvmStatic
-        @Parameterized.Parameters(name = "length={0} styleCount={1} hasMetricAffectingStyle={2}")
+        @Parameterized.Parameters(name = "length={0} styleCount={1}")
         fun initParameters() = cartesian(
-                arrayOf(8, 64, 512),
-                arrayOf(8, 64, 512),
-                arrayOf(true, false)
+                arrayOf(32, 512),
+                arrayOf(32, 512)
             )
     }
 
     @Test
     fun first_compose() {
-        textGeneratorRule.generator { textGenerator ->
+        textBenchmarkRule.generator { textGenerator ->
             benchmarkRule.measureFirstCompose(
                 activity,
                 TextMultiStyleTestCase(
                     activity,
                     textLength,
                     styleCount,
-                    hasMetricAffectingStyle,
                     textGenerator
                 )
             )
@@ -85,14 +82,13 @@
 
     @Test
     fun first_measure() {
-        textGeneratorRule.generator { textGenerator ->
+        textBenchmarkRule.generator { textGenerator ->
             benchmarkRule.measureFirstMeasure(
                 activity,
                 TextMultiStyleTestCase(
                     activity,
                     textLength,
                     styleCount,
-                    hasMetricAffectingStyle,
                     textGenerator
                 )
             )
@@ -101,14 +97,13 @@
 
     @Test
     fun first_layout() {
-        textGeneratorRule.generator { textGenerator ->
+        textBenchmarkRule.generator { textGenerator ->
             benchmarkRule.measureFirstLayout(
                 activity,
                 TextMultiStyleTestCase(
                     activity,
                     textLength,
                     styleCount,
-                    hasMetricAffectingStyle,
                     textGenerator
                 )
             )
@@ -117,14 +112,13 @@
 
     @Test
     fun first_draw() {
-        textGeneratorRule.generator { textGenerator ->
+        textBenchmarkRule.generator { textGenerator ->
             benchmarkRule.measureFirstDraw(
                 activity,
                 TextMultiStyleTestCase(
                     activity,
                     textLength,
                     styleCount,
-                    hasMetricAffectingStyle,
                     textGenerator
                 )
             )
@@ -133,14 +127,13 @@
 
     @Test
     fun layout() {
-        textGeneratorRule.generator { textGenerator ->
+        textBenchmarkRule.generator { textGenerator ->
             benchmarkRule.measureLayoutPerf(
                 activity,
                 TextMultiStyleTestCase(
                     activity,
                     textLength,
                     styleCount,
-                    hasMetricAffectingStyle,
                     textGenerator
                 )
             )
@@ -149,14 +142,13 @@
 
     @Test
     fun draw() {
-        textGeneratorRule.generator { textGenerator ->
+        textBenchmarkRule.generator { textGenerator ->
             benchmarkRule.measureDrawPerf(
                 activity,
                 TextMultiStyleTestCase(
                     activity,
                     textLength,
                     styleCount,
-                    hasMetricAffectingStyle,
                     textGenerator
                 )
             )
diff --git a/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/core/TextWithSpanBenchmark.kt b/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/core/TextWithSpanBenchmark.kt
index 48d1152..2fd7bf0 100644
--- a/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/core/TextWithSpanBenchmark.kt
+++ b/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/core/TextWithSpanBenchmark.kt
@@ -28,7 +28,7 @@
 import androidx.ui.benchmark.measureFirstMeasure
 import androidx.ui.benchmark.measureLayoutPerf
 import androidx.ui.test.DisableTransitions
-import androidx.ui.test.RandomTextGeneratorTestRule
+import androidx.ui.test.TextBenchmarkTestRule
 import androidx.ui.test.cartesian
 import org.junit.Rule
 import org.junit.Test
@@ -39,8 +39,7 @@
 @LargeTest
 @RunWith(Parameterized::class)
 class TextWithSpanBenchmark(
-    private val textLength: Int,
-    private val hasMetricAffectingStyle: Boolean
+    private val textLength: Int
 ) {
     @get:Rule
     val benchmarkRule = BenchmarkRule()
@@ -52,28 +51,26 @@
     val disableAnimationRule = DisableTransitions()
 
     @get:Rule
-    val textGeneratorRule = RandomTextGeneratorTestRule()
+    val textBenchmarkRule = TextBenchmarkTestRule()
 
     private val activity: Activity get() = activityRule.activity
 
     companion object {
         @JvmStatic
-        @Parameterized.Parameters(name = "length={0} hasMetricAffectingStyle={1}")
+        @Parameterized.Parameters(name = "length={0}")
         fun initParameters() = cartesian(
-            arrayOf(8, 64, 512),
-            arrayOf(true, false)
+            arrayOf(32, 512)
         )
     }
 
     @Test
     fun first_compose() {
-        textGeneratorRule.generator { textGenerator ->
+        textBenchmarkRule.generator { textGenerator ->
             benchmarkRule.measureFirstCompose(
                 activity,
                 TextWithSpanTestCase(
                     activity,
                     textLength,
-                    hasMetricAffectingStyle,
                     textGenerator
                 )
             )
@@ -82,13 +79,12 @@
 
     @Test
     fun first_measure() {
-        textGeneratorRule.generator { textGenerator ->
+        textBenchmarkRule.generator { textGenerator ->
             benchmarkRule.measureFirstMeasure(
                 activity,
                 TextWithSpanTestCase(
                     activity,
                     textLength,
-                    hasMetricAffectingStyle,
                     textGenerator
                 )
             )
@@ -97,13 +93,12 @@
 
     @Test
     fun first_layout() {
-        textGeneratorRule.generator { textGenerator ->
+        textBenchmarkRule.generator { textGenerator ->
             benchmarkRule.measureFirstLayout(
                 activity,
                 TextWithSpanTestCase(
                     activity,
                     textLength,
-                    hasMetricAffectingStyle,
                     textGenerator
                 )
             )
@@ -112,13 +107,12 @@
 
     @Test
     fun first_draw() {
-        textGeneratorRule.generator { textGenerator ->
+        textBenchmarkRule.generator { textGenerator ->
             benchmarkRule.measureFirstDraw(
                 activity,
                 TextWithSpanTestCase(
                     activity,
                     textLength,
-                    hasMetricAffectingStyle,
                     textGenerator
                 )
             )
@@ -127,13 +121,12 @@
 
     @Test
     fun layout() {
-        textGeneratorRule.generator { textGenerator ->
+        textBenchmarkRule.generator { textGenerator ->
             benchmarkRule.measureLayoutPerf(
                 activity,
                 TextWithSpanTestCase(
                     activity,
                     textLength,
-                    hasMetricAffectingStyle,
                     textGenerator
                 )
             )
@@ -142,13 +135,12 @@
 
     @Test
     fun draw() {
-        textGeneratorRule.generator { textGenerator ->
+        textBenchmarkRule.generator { textGenerator ->
             benchmarkRule.measureDrawPerf(
                 activity,
                 TextWithSpanTestCase(
                     activity,
                     textLength,
-                    hasMetricAffectingStyle,
                     textGenerator
                 )
             )
diff --git a/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/text/ParagraphBenchmark.kt b/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/text/ParagraphBenchmark.kt
index a471008..d60e197 100644
--- a/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/text/ParagraphBenchmark.kt
+++ b/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/text/ParagraphBenchmark.kt
@@ -24,7 +24,7 @@
 import androidx.ui.core.sp
 import androidx.ui.test.Alphabet
 import androidx.ui.test.RandomTextGenerator
-import androidx.ui.test.RandomTextGeneratorTestRule
+import androidx.ui.test.TextBenchmarkTestRule
 import androidx.ui.test.TextType
 import androidx.ui.test.cartesian
 import androidx.ui.text.font.Font
@@ -45,7 +45,7 @@
         @JvmStatic
         @Parameterized.Parameters(name = "length={0} type={1} alphabet={2}")
         fun initParameters(): List<Array<Any>> = cartesian(
-            arrayOf(32, 128, 512),
+            arrayOf(32, 512),
             arrayOf(TextType.PlainText, TextType.StyledText),
             arrayOf(Alphabet.Latin, Alphabet.Cjk)
         )
@@ -55,7 +55,7 @@
     val benchmarkRule = BenchmarkRule()
 
     @get:Rule
-    val textGeneratorRule = RandomTextGeneratorTestRule(alphabet)
+    val textBenchmarkRule = TextBenchmarkTestRule(alphabet)
 
     // dummy object required to construct Paragraph
     private val resourceLoader = object : Font.ResourceLoader {
@@ -87,7 +87,7 @@
     fun minIntrinsicWidth() {
         benchmarkRule.measureRepeated {
             val paragraph = runWithTimingDisabled {
-                textGeneratorRule.generator { textGenerator ->
+                textBenchmarkRule.generator { textGenerator ->
                     paragraph(textGenerator)
                 }
             }
@@ -100,7 +100,7 @@
     fun layout() {
         benchmarkRule.measureRepeated {
             val pair = runWithTimingDisabled {
-                textGeneratorRule.generator { textGenerator ->
+                textBenchmarkRule.generator { textGenerator ->
                     val paragraph = paragraph(textGenerator)
                     paragraph.layout(ParagraphConstraints(Float.MAX_VALUE))
                     // create a new paragraph and use a smaller width to get
diff --git a/ui/integration-tests/test/src/main/java/androidx/ui/core/TextBasicTestCase.kt b/ui/integration-tests/test/src/main/java/androidx/ui/core/TextBasicTestCase.kt
index dfb4d0e..d000723 100644
--- a/ui/integration-tests/test/src/main/java/androidx/ui/core/TextBasicTestCase.kt
+++ b/ui/integration-tests/test/src/main/java/androidx/ui/core/TextBasicTestCase.kt
@@ -20,7 +20,9 @@
 import android.view.ViewGroup
 import androidx.compose.composer
 import androidx.ui.graphics.Color
-import androidx.ui.material.MaterialTheme
+import androidx.ui.layout.ConstrainedBox
+import androidx.ui.layout.DpConstraints
+import androidx.ui.layout.Wrap
 import androidx.ui.test.ComposeTestCase
 import androidx.ui.test.RandomTextGenerator
 import androidx.ui.text.TextStyle
@@ -39,8 +41,10 @@
     }
 
     override fun setComposeContent(activity: Activity) = activity.setContent {
-        MaterialTheme {
-            Text(text = text, style = TextStyle(color = Color.Black, fontSize = 14.sp))
+        Wrap {
+            ConstrainedBox(constraints = DpConstraints.tightConstraintsForWidth(160.dp)) {
+                Text(text = text, style = TextStyle(color = Color.Black, fontSize = 8.sp))
+            }
         }
     }!!
 }
\ No newline at end of file
diff --git a/ui/integration-tests/test/src/main/java/androidx/ui/core/TextMultiStyleTestCase.kt b/ui/integration-tests/test/src/main/java/androidx/ui/core/TextMultiStyleTestCase.kt
index 6490de2..06abd8a 100644
--- a/ui/integration-tests/test/src/main/java/androidx/ui/core/TextMultiStyleTestCase.kt
+++ b/ui/integration-tests/test/src/main/java/androidx/ui/core/TextMultiStyleTestCase.kt
@@ -20,7 +20,9 @@
 import android.view.ViewGroup
 import androidx.compose.composer
 import androidx.ui.graphics.Color
-import androidx.ui.material.MaterialTheme
+import androidx.ui.layout.ConstrainedBox
+import androidx.ui.layout.DpConstraints
+import androidx.ui.layout.Wrap
 import androidx.ui.test.ComposeTestCase
 import androidx.ui.test.RandomTextGenerator
 import androidx.ui.text.AnnotatedString
@@ -30,7 +32,6 @@
     activity: Activity,
     private val textLength: Int,
     private val styleCount: Int,
-    private val hasMetricAffectingStyle: Boolean,
     private val randomTextGenerator: RandomTextGenerator
 ) : ComposeTestCase(activity) {
 
@@ -40,14 +41,16 @@
         text = randomTextGenerator.nextAnnotatedString(
             length = textLength,
             styleCount = styleCount,
-            hasMetricAffectingStyle = hasMetricAffectingStyle
+            hasMetricAffectingStyle = true
         )
         return super.setupContentInternal(activity)
     }
 
     override fun setComposeContent(activity: Activity) = activity.setContent {
-        MaterialTheme {
-            Text(text = text, style = TextStyle(color = Color.Black, fontSize = 14.sp))
+        Wrap {
+            ConstrainedBox(constraints = DpConstraints.tightConstraintsForWidth(160.dp)) {
+                Text(text = text, style = TextStyle(color = Color.Black, fontSize = 8.sp))
+            }
         }
     }!!
 }
\ No newline at end of file
diff --git a/ui/integration-tests/test/src/main/java/androidx/ui/core/TextWithSpanTestCase.kt b/ui/integration-tests/test/src/main/java/androidx/ui/core/TextWithSpanTestCase.kt
index 80f93c3..f2d5275 100644
--- a/ui/integration-tests/test/src/main/java/androidx/ui/core/TextWithSpanTestCase.kt
+++ b/ui/integration-tests/test/src/main/java/androidx/ui/core/TextWithSpanTestCase.kt
@@ -20,7 +20,9 @@
 import android.view.ViewGroup
 import androidx.compose.composer
 import androidx.ui.graphics.Color
-import androidx.ui.material.MaterialTheme
+import androidx.ui.layout.ConstrainedBox
+import androidx.ui.layout.DpConstraints
+import androidx.ui.layout.Wrap
 import androidx.ui.test.ComposeTestCase
 import androidx.ui.test.RandomTextGenerator
 import androidx.ui.text.TextStyle
@@ -28,7 +30,6 @@
 class TextWithSpanTestCase(
     activity: Activity,
     private val textLength: Int,
-    private val hasMetricAffectingStyle: Boolean,
     private val randomTextGenerator: RandomTextGenerator
 ) : ComposeTestCase(activity) {
 
@@ -37,16 +38,18 @@
     override fun setupContentInternal(activity: Activity): ViewGroup {
         textPieces = randomTextGenerator.nextStyledWordList(
             length = textLength,
-            hasMetricAffectingStyle = hasMetricAffectingStyle
+            hasMetricAffectingStyle = true
         )
         return super.setupContentInternal(activity)
     }
 
     override fun setComposeContent(activity: Activity) = activity.setContent {
-        MaterialTheme {
-            Text(style = TextStyle(color = Color.Black, fontSize = 14.sp)) {
-                textPieces.forEach { (text, style) ->
-                    Span(text = text, style = style)
+        Wrap {
+            ConstrainedBox(constraints = DpConstraints.tightConstraintsForWidth(160.dp)) {
+                Text(style = TextStyle(color = Color.Black, fontSize = 8.sp)) {
+                    textPieces.forEach { (text, style) ->
+                        Span(text = text, style = style)
+                    }
                 }
             }
         }
diff --git a/ui/integration-tests/test/src/main/java/androidx/ui/test/RandomTextGeneratorTestRule.kt b/ui/integration-tests/test/src/main/java/androidx/ui/test/RandomTextGeneratorTestRule.kt
deleted file mode 100644
index 8265c50..0000000
--- a/ui/integration-tests/test/src/main/java/androidx/ui/test/RandomTextGeneratorTestRule.kt
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright 2019 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.ui.test
-
-import org.junit.rules.TestRule
-import org.junit.runner.Description
-import org.junit.runners.model.Statement
-import kotlin.random.Random
-
-/**
- * Test rule that initiates a [RandomTextGenerator] using a different seed based on the class and
- * function name. This way each function will have a different text generated, but at each run
- * the same function will get the same text.
- *
- * This will ensure that the execution order of a test class or functions in a test class does
- * not affect others because of the native text layout cache.
- */
-class RandomTextGeneratorTestRule(private val alphabet: Alphabet = Alphabet.Latin) : TestRule {
-    private lateinit var textGenerator: RandomTextGenerator
-
-    override fun apply(base: Statement, description: Description): Statement =
-        object : Statement() {
-            override fun evaluate() {
-                // gives the full class and function name including the parameters
-                val fullName = "${description.className}#${description.methodName}"
-
-                textGenerator = RandomTextGenerator(alphabet, Random(fullName.hashCode()))
-
-                base.evaluate()
-            }
-        }
-
-    fun <T> generator(block: (generator: RandomTextGenerator) -> T): T {
-        return block(textGenerator)
-    }
-}
diff --git a/ui/integration-tests/test/src/main/java/androidx/ui/test/TextBenchmarkTestRule.kt b/ui/integration-tests/test/src/main/java/androidx/ui/test/TextBenchmarkTestRule.kt
new file mode 100644
index 0000000..23e4653
--- /dev/null
+++ b/ui/integration-tests/test/src/main/java/androidx/ui/test/TextBenchmarkTestRule.kt
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2019 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.ui.test
+
+import android.graphics.Canvas
+import android.util.Log
+import org.junit.rules.RuleChain
+import org.junit.rules.TestRule
+import org.junit.runner.Description
+import org.junit.runners.model.Statement
+import kotlin.random.Random
+
+/**
+ * Collection of text benchmark utilities. It tries to
+ * - trigger garbage collection
+ * - free text layout caches
+ * before each test run.
+ *
+ * It also provides random text generation capabilities.
+ *
+ */
+class TextBenchmarkTestRule(alphabet: Alphabet = Alphabet.Latin) : TestRule {
+    private val textGeneratorTestRule = RandomTextGeneratorTestRule(alphabet)
+
+    override fun apply(base: Statement, description: Description): Statement {
+        return RuleChain
+            .outerRule(GarbageCollectTestRule())
+            .around(TextLayoutCacheTestRule())
+            .around(textGeneratorTestRule)
+            .apply(base, description)
+    }
+
+    fun <T> generator(block: (generator: RandomTextGenerator) -> T): T {
+        return textGeneratorTestRule.generator { textGenerator ->
+            block(textGenerator)
+        }
+    }
+}
+
+/**
+ * At the beginning of each test calls Canvas.freeTextLayoutCaches in order to clear the native
+ * text layout cache.
+ */
+private class TextLayoutCacheTestRule : TestRule {
+    private val TAG = "TextLayoutCacheTestRule"
+
+    override fun apply(base: Statement, description: Description): Statement =
+        object : Statement() {
+            override fun evaluate() {
+                tryFreeTextLayoutCache()
+                base.evaluate()
+            }
+        }
+
+    fun tryFreeTextLayoutCache() {
+        try {
+            val freeCaches = Canvas::class.java.getDeclaredMethod("freeTextLayoutCaches")
+            freeCaches.isAccessible = true
+            freeCaches.invoke(null)
+        } catch (e: Exception) {
+            Log.w(TAG, "Cannot fre text layout cache", e)
+            // ignore
+        }
+    }
+}
+
+/**
+ * At the beginning of each test calls Runtime.getRuntime().gc() in order to free memory and
+ * possibly prevent GC during measurement.
+ */
+private class GarbageCollectTestRule : TestRule {
+    override fun apply(base: Statement, description: Description): Statement =
+        object : Statement() {
+            override fun evaluate() {
+                Runtime.getRuntime().gc()
+                base.evaluate()
+            }
+        }
+}
+
+/**
+ * Test rule that initiates a [RandomTextGenerator] using a different seed based on the class and
+ * function name. This way each function will have a different text generated, but at each run
+ * the same function will get the same text.
+ *
+ * This will ensure that the execution order of a test class or functions in a test class does
+ * not affect others because of the native text layout cache.
+ */
+private class RandomTextGeneratorTestRule(
+    private val alphabet: Alphabet = Alphabet.Latin
+) : TestRule {
+    private lateinit var textGenerator: RandomTextGenerator
+
+    override fun apply(base: Statement, description: Description): Statement =
+        object : Statement() {
+            override fun evaluate() {
+                // gives the full class and function name including the parameters
+                val fullName = "${description.className}#${description.methodName}"
+
+                textGenerator = RandomTextGenerator(alphabet, Random(fullName.hashCode()))
+
+                base.evaluate()
+            }
+        }
+
+    fun <T> generator(block: (generator: RandomTextGenerator) -> T): T {
+        return block(textGenerator)
+    }
+}
\ No newline at end of file
diff --git a/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/BaseSimpleRadioButtonTestCase.kt b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/BaseSimpleRadioButtonTestCase.kt
new file mode 100644
index 0000000..e854f7d1
--- /dev/null
+++ b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/BaseSimpleRadioButtonTestCase.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2019 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.ui.test.cases
+
+import android.app.Activity
+import androidx.compose.FrameManager
+import androidx.compose.State
+import androidx.compose.state
+import androidx.compose.unaryPlus
+import androidx.ui.core.Dp
+import androidx.ui.core.dp
+import androidx.ui.test.ComposeTestCase
+import androidx.ui.test.ToggleableTestCase
+
+abstract class BaseSimpleRadioButtonTestCase(
+    activity: Activity
+) : ComposeTestCase(activity), ToggleableTestCase {
+
+    private var state: State<Dp>? = null
+
+    fun getInnerSize(): State<Dp> {
+        val innerSize = +state { 10.dp }
+        state = innerSize
+        return innerSize
+    }
+
+    override fun toggleState() {
+        with(state!!) {
+            value = if (value == 10.dp) {
+                20.dp
+            } else {
+                10.dp
+            }
+        }
+        FrameManager.nextFrame()
+    }
+}
diff --git a/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/NestedScrollerTestCase.kt b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/NestedScrollerTestCase.kt
index a975728..b03ca4f 100644
--- a/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/NestedScrollerTestCase.kt
+++ b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/NestedScrollerTestCase.kt
@@ -31,6 +31,7 @@
 import androidx.ui.layout.Column
 import androidx.ui.layout.CrossAxisAlignment
 import androidx.ui.layout.FlexColumn
+import androidx.ui.layout.LayoutSize
 import androidx.ui.foundation.HorizontalScroller
 import androidx.ui.layout.Row
 import androidx.ui.foundation.ScrollerPosition
@@ -54,7 +55,7 @@
         MaterialTheme {
             Surface {
                 VerticalScroller {
-                    Column {
+                    Column(mainAxisSize = LayoutSize.Expand) {
                         repeat(5) { index ->
                             SquareRow(index == 0)
                         }
@@ -73,7 +74,7 @@
     fun SquareRow(useScrollerPosition: Boolean) {
         val playStoreColor = Color(red = 0x00, green = 0x00, blue = 0x80)
         val content = @Composable {
-            Row {
+            Row(mainAxisSize = LayoutSize.Expand) {
                 repeat(6) {
                     WithDensity {
                         FlexColumn(crossAxisAlignment = CrossAxisAlignment.Start) {
@@ -92,7 +93,10 @@
                                     text = "Some title",
                                     style = TextStyle(Color.Black, 60.px.toSp())
                                 )
-                                Row(crossAxisAlignment = CrossAxisAlignment.Center) {
+                                Row(
+                                    mainAxisSize = LayoutSize.Expand,
+                                    crossAxisAlignment = CrossAxisAlignment.Center
+                                ) {
                                     Text("3.5 ★", TextStyle(fontSize = 40.px.toSp()))
                                     ColoredRect(
                                         width = 40.px.toDp(),
diff --git a/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/ScrollerTestCase.kt b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/ScrollerTestCase.kt
index 48fdd1d..be4b757 100644
--- a/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/ScrollerTestCase.kt
+++ b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/ScrollerTestCase.kt
@@ -32,6 +32,7 @@
 import androidx.ui.layout.Column
 import androidx.ui.layout.Container
 import androidx.ui.layout.CrossAxisAlignment
+import androidx.ui.layout.LayoutSize
 import androidx.ui.foundation.ScrollerPosition
 import androidx.ui.foundation.VerticalScroller
 import androidx.ui.graphics.Paint
@@ -51,7 +52,10 @@
         VerticalScroller(
             scrollerPosition = scrollerPosition
         ) {
-            Column(crossAxisAlignment = CrossAxisAlignment.Start) {
+            Column(
+                mainAxisSize = LayoutSize.Expand,
+                crossAxisAlignment = CrossAxisAlignment.Start
+            ) {
                 for (green in 0..0xFF) {
                     ColorStripe(0xFF, green, 0)
                 }
diff --git a/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/SimpleRadioButton1TestCase.kt b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/SimpleRadioButton1TestCase.kt
new file mode 100644
index 0000000..48ba67e
--- /dev/null
+++ b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/SimpleRadioButton1TestCase.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2019 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.ui.test.cases
+
+import android.app.Activity
+import androidx.compose.composer
+import androidx.ui.core.dp
+import androidx.ui.core.setContent
+import androidx.ui.foundation.shape.DrawShape
+import androidx.ui.foundation.shape.border.Border
+import androidx.ui.foundation.shape.border.DrawBorder
+import androidx.ui.foundation.shape.corner.CircleShape
+import androidx.ui.graphics.Color
+import androidx.ui.layout.Container
+
+class SimpleRadioButton1TestCase(
+    activity: Activity
+) : BaseSimpleRadioButtonTestCase(activity) {
+
+    override fun setComposeContent(activity: Activity) = activity.setContent {
+        Container(width = 48.dp, height = 48.dp) {
+            DrawBorder(CircleShape, Border(Color.Cyan, 1.dp))
+            val innerSize = getInnerSize().value
+            Container(width = innerSize, height = innerSize) {
+                DrawShape(CircleShape, Color.Cyan)
+            }
+        }
+    }!!
+}
diff --git a/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/SimpleRadioButton2TestCase.kt b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/SimpleRadioButton2TestCase.kt
new file mode 100644
index 0000000..7623891
--- /dev/null
+++ b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/SimpleRadioButton2TestCase.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2019 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.ui.test.cases
+
+import android.app.Activity
+import androidx.compose.composer
+import androidx.ui.core.Density
+import androidx.ui.core.Dp
+import androidx.ui.core.Px
+import androidx.ui.core.PxSize
+import androidx.ui.core.dp
+import androidx.ui.core.setContent
+import androidx.ui.core.withDensity
+import androidx.ui.engine.geometry.Offset
+import androidx.ui.engine.geometry.Outline
+import androidx.ui.engine.geometry.Shape
+import androidx.ui.engine.geometry.shift
+import androidx.ui.foundation.shape.DrawShape
+import androidx.ui.foundation.shape.border.Border
+import androidx.ui.foundation.shape.border.DrawBorder
+import androidx.ui.foundation.shape.corner.CircleShape
+import androidx.ui.graphics.Color
+import androidx.ui.graphics.Path
+import androidx.ui.layout.Container
+
+class SimpleRadioButton2TestCase(
+    activity: Activity
+) : BaseSimpleRadioButtonTestCase(activity) {
+
+    override fun setComposeContent(activity: Activity) = activity.setContent {
+        Container(width = 48.dp, height = 48.dp) {
+            DrawBorder(CircleShape, Border(Color.Cyan, 1.dp))
+            val padding = (48.dp - getInnerSize().value) / 2
+            DrawShape(PaddingShape(padding, CircleShape), Color.Cyan)
+        }
+    }!!
+}
+
+private data class PaddingShape(val padding: Dp, val shape: Shape) : Shape {
+    override fun createOutline(size: PxSize, density: Density): Outline {
+        val twoPaddings = withDensity(density) { (padding * 2).toPx() }
+        val sizeMinusPaddings = PxSize(size.width - twoPaddings, size.height - twoPaddings)
+        val rawResult = shape.createOutline(sizeMinusPaddings, density)
+        return rawResult.offset(twoPaddings / 2)
+    }
+}
+
+private fun Outline.offset(size: Px): Outline {
+    val offset = Offset(size.value, size.value)
+    return when (this) {
+        is Outline.Rectangle -> Outline.Rectangle(rect.shift(offset))
+        is Outline.Rounded -> Outline.Rounded(rrect.shift(offset))
+        is Outline.Generic -> Outline.Generic(Path().apply {
+            addPath(path)
+            shift(offset)
+        })
+    }
+}
diff --git a/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/SimpleRadioButton3TestCase.kt b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/SimpleRadioButton3TestCase.kt
new file mode 100644
index 0000000..9c505c6
--- /dev/null
+++ b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/SimpleRadioButton3TestCase.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2019 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.ui.test.cases
+
+import android.app.Activity
+import androidx.compose.composer
+import androidx.compose.memo
+import androidx.compose.unaryPlus
+import androidx.ui.core.Draw
+import androidx.ui.core.PxSize
+import androidx.ui.core.dp
+import androidx.ui.core.minDimension
+import androidx.ui.core.setContent
+import androidx.ui.engine.geometry.Offset
+import androidx.ui.graphics.Canvas
+import androidx.ui.graphics.Paint
+import androidx.ui.graphics.PaintingStyle
+import androidx.ui.layout.Container
+
+class SimpleRadioButton3TestCase(
+    activity: Activity
+) : BaseSimpleRadioButtonTestCase(activity) {
+
+    override fun setComposeContent(activity: Activity) = activity.setContent {
+        Container(width = 48.dp, height = 48.dp) {
+            val innerSize = getInnerSize()
+            val borderPaint = +memo { Paint().apply { style = PaintingStyle.stroke } }
+            val fillPaint = +memo { Paint() }
+            Draw { canvas: Canvas, parentSize: PxSize ->
+                val center = Offset(parentSize.width.value / 2f, parentSize.height.value / 2f)
+                canvas.drawCircle(center, parentSize.minDimension.value, borderPaint)
+                val innerRadius = innerSize.value.value / 2f
+                canvas.drawCircle(center, innerRadius, fillPaint)
+            }
+        }
+    }!!
+}
diff --git a/ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/ui/animation/demos/AnimatableSeekBar.kt b/ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/ui/animation/demos/AnimatableSeekBar.kt
index b4dfff8..3bbc4cb 100644
--- a/ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/ui/animation/demos/AnimatableSeekBar.kt
+++ b/ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/ui/animation/demos/AnimatableSeekBar.kt
@@ -38,6 +38,7 @@
 import androidx.ui.graphics.Color
 import androidx.ui.layout.Column
 import androidx.ui.layout.Container
+import androidx.ui.layout.LayoutSize
 import androidx.ui.layout.Padding
 import androidx.ui.graphics.Paint
 import androidx.ui.text.TextStyle
@@ -47,7 +48,7 @@
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
         setContent {
-            Column {
+            Column(mainAxisSize = LayoutSize.Expand) {
                 Padding(40.dp) {
                     Text("Drag or tap on the seek bar", style = TextStyle(fontSize = 8.sp))
                 }
diff --git a/ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/ui/animation/demos/FancyScrolling.kt b/ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/ui/animation/demos/FancyScrolling.kt
index e3f5539..ef30435 100644
--- a/ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/ui/animation/demos/FancyScrolling.kt
+++ b/ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/ui/animation/demos/FancyScrolling.kt
@@ -42,6 +42,7 @@
 import androidx.ui.engine.geometry.Rect
 import androidx.ui.graphics.Color
 import androidx.ui.layout.Column
+import androidx.ui.layout.LayoutSize
 import androidx.ui.layout.Padding
 import androidx.ui.graphics.Canvas
 import androidx.ui.graphics.Paint
@@ -59,7 +60,7 @@
 
     @Composable
     fun FancyScrollingExample() {
-        Column {
+        Column(mainAxisSize = LayoutSize.Expand) {
             Padding(40.dp) {
                 Text("<== Scroll horizontally ==>", style = TextStyle(fontSize = 20.sp))
             }
diff --git a/ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/ui/animation/demos/RepeatedRotationActivity.kt b/ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/ui/animation/demos/RepeatedRotationActivity.kt
index e29d21d..1223527 100644
--- a/ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/ui/animation/demos/RepeatedRotationActivity.kt
+++ b/ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/ui/animation/demos/RepeatedRotationActivity.kt
@@ -30,6 +30,7 @@
 import androidx.ui.layout.Center
 import androidx.ui.layout.Column
 import androidx.ui.layout.Container
+import androidx.ui.layout.LayoutSize
 import androidx.ui.layout.MainAxisAlignment
 import androidx.ui.graphics.Color
 import androidx.ui.graphics.Paint
@@ -53,7 +54,10 @@
 fun RepeatedRotation() {
     Center {
         val state = +state { RotationStates.Original }
-        Column(mainAxisAlignment = MainAxisAlignment.SpaceEvenly) {
+        Column(
+            mainAxisSize = LayoutSize.Expand,
+            mainAxisAlignment = MainAxisAlignment.SpaceEvenly
+        ) {
             val textStyle = TextStyle(fontSize = 18.sp)
             PressReleasedGestureDetector(>
                 state.value = RotationStates.Rotated
diff --git a/ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/ui/animation/demos/SpringBackScrolling.kt b/ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/ui/animation/demos/SpringBackScrolling.kt
index 8b668d8..36b937b 100644
--- a/ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/ui/animation/demos/SpringBackScrolling.kt
+++ b/ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/ui/animation/demos/SpringBackScrolling.kt
@@ -42,6 +42,7 @@
 import androidx.ui.engine.geometry.Rect
 import androidx.ui.graphics.Color
 import androidx.ui.layout.Column
+import androidx.ui.layout.LayoutSize
 import androidx.ui.layout.Padding
 import androidx.ui.graphics.Canvas
 import androidx.ui.graphics.Paint
@@ -59,7 +60,7 @@
 
     @Composable
     fun SpringBackExample() {
-        Column {
+        Column(mainAxisSize = LayoutSize.Expand) {
             Padding(40.dp) {
                 Text("<== Scroll horizontally ==>", style = TextStyle(fontSize = 20.sp))
             }
diff --git a/ui/ui-foundation/integration-tests/foundation-demos/src/main/java/androidx/ui/foundation/demos/PopupActivity.kt b/ui/ui-foundation/integration-tests/foundation-demos/src/main/java/androidx/ui/foundation/demos/PopupActivity.kt
index 9d59c2d..3128c74f 100644
--- a/ui/ui-foundation/integration-tests/foundation-demos/src/main/java/androidx/ui/foundation/demos/PopupActivity.kt
+++ b/ui/ui-foundation/integration-tests/foundation-demos/src/main/java/androidx/ui/foundation/demos/PopupActivity.kt
@@ -68,7 +68,7 @@
             val exampleIndex = +state { 0 }
             val totalExamples = 9
 
-            Column(mainAxisSize = LayoutSize.Wrap, crossAxisAlignment = CrossAxisAlignment.Center) {
+            Column(crossAxisAlignment = CrossAxisAlignment.Center) {
                 FlexRow(
                     mainAxisSize = LayoutSize.Expand,
                     mainAxisAlignment = MainAxisAlignment.SpaceBetween
@@ -192,7 +192,10 @@
 @Composable
 fun PopupWithChangingContent() {
     Container {
-        Column(crossAxisAlignment = CrossAxisAlignment.Center) {
+        Column(
+            mainAxisSize = LayoutSize.Expand,
+            crossAxisAlignment = CrossAxisAlignment.Center
+        ) {
             val heightSize = 120.dp
             val widthSize = 160.dp
             val popupContentState = +state { 0 }
@@ -244,7 +247,10 @@
     val parentHeight = +state { 60.dp }
     val parentSizeChanged = +state { false }
 
-    Column(crossAxisAlignment = CrossAxisAlignment.Center) {
+    Column(
+        mainAxisSize = LayoutSize.Expand,
+        crossAxisAlignment = CrossAxisAlignment.Center
+    ) {
         Container(
             height = containerHeight,
             width = containerWidth,
@@ -295,7 +301,10 @@
 @Composable
 fun PopupDropdownAlignment() {
     Container {
-        Column(crossAxisAlignment = CrossAxisAlignment.Center) {
+        Column(
+            mainAxisSize = LayoutSize.Expand,
+            crossAxisAlignment = CrossAxisAlignment.Center
+        ) {
             val heightSize = 120.dp
             val widthSize = 160.dp
             val dropDownAlignment = +state { DropDownAlignment.Left }
@@ -341,7 +350,10 @@
         val counter = +state { 0 }
         val popupAlignment = +state { Alignment.TopLeft }
 
-        Column(crossAxisAlignment = CrossAxisAlignment.Center) {
+        Column(
+            mainAxisSize = LayoutSize.Expand,
+            crossAxisAlignment = CrossAxisAlignment.Center
+        ) {
             ColoredContainer(
                 height = heightSize,
                 width = widthSize,
@@ -381,7 +393,10 @@
 @Composable
 fun PopupWithEditText() {
     Container {
-        Column(crossAxisAlignment = CrossAxisAlignment.Center) {
+        Column(
+            mainAxisSize = LayoutSize.Expand,
+            crossAxisAlignment = CrossAxisAlignment.Center
+        ) {
             val widthSize = 190.dp
             val heightSize = 120.dp
             val editLineSize = 150.dp
@@ -428,7 +443,10 @@
 @Composable
 fun PopupWithChangingSize() {
     Container {
-        Column(crossAxisAlignment = CrossAxisAlignment.Center) {
+        Column(
+            mainAxisSize = LayoutSize.Expand,
+            crossAxisAlignment = CrossAxisAlignment.Center
+        ) {
             val showPopup = +state { true }
             val heightSize = 120.dp
             val widthSize = 160.dp
@@ -485,7 +503,10 @@
     val widthSize = 200.dp
     Container(width = widthSize, height = heightSize) {
         VerticalScroller {
-            Column(crossAxisAlignment = CrossAxisAlignment.Center) {
+            Column(
+                mainAxisSize = LayoutSize.Expand,
+                crossAxisAlignment = CrossAxisAlignment.Center
+            ) {
                 ColoredContainer(
                     width = 80.dp,
                     height = 160.dp,
@@ -507,7 +528,10 @@
 @Composable
 fun PopupOnKeyboardUp() {
     Container {
-        Column(crossAxisAlignment = CrossAxisAlignment.Center) {
+        Column(
+            mainAxisSize = LayoutSize.Expand,
+            crossAxisAlignment = CrossAxisAlignment.Center
+        ) {
             val widthSize = 190.dp
             val heightSize = 120.dp
 
diff --git a/ui/ui-foundation/integration-tests/samples/src/main/java/androidx/ui/foundation/samples/ScrollerSamples.kt b/ui/ui-foundation/integration-tests/samples/src/main/java/androidx/ui/foundation/samples/ScrollerSamples.kt
index 8866884..3a146a2 100644
--- a/ui/ui-foundation/integration-tests/samples/src/main/java/androidx/ui/foundation/samples/ScrollerSamples.kt
+++ b/ui/ui-foundation/integration-tests/samples/src/main/java/androidx/ui/foundation/samples/ScrollerSamples.kt
@@ -48,6 +48,7 @@
 import androidx.ui.layout.EdgeInsets
 import androidx.ui.layout.FlexColumn
 import androidx.ui.layout.HeightSpacer
+import androidx.ui.layout.LayoutSize
 import androidx.ui.layout.Stack
 import androidx.ui.layout.Table
 import androidx.ui.layout.Wrap
@@ -104,7 +105,7 @@
     // Scroller will be clipped to this padding
     Padding(padding = 10.dp) {
         VerticalScroller {
-            Column {
+            Column(mainAxisSize = LayoutSize.Expand) {
                 phrases.forEach { phrase ->
                     Text(text = phrase, style = style)
                 }
@@ -117,7 +118,7 @@
 @Composable
 fun SimpleHorizontalScrollerSample() {
     HorizontalScroller {
-        Row {
+        Row(mainAxisSize = LayoutSize.Expand) {
             repeat(100) { index ->
                 Square(index)
             }
@@ -131,9 +132,9 @@
     // Create and own ScrollerPosition to call `smoothScrollTo` later
     val position = +memo { ScrollerPosition() }
     val scrollable = +state { true }
-    Column {
+    Column(mainAxisSize = LayoutSize.Expand) {
         HorizontalScroller(scrollerPosition = position, isScrollable = scrollable.value) {
-            Row {
+            Row(mainAxisSize = LayoutSize.Expand) {
                 repeat(1000) { index ->
                     Square(index)
                 }
diff --git a/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/SemanticsL1.kt b/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/SemanticsL1.kt
index a24fa7b..9ca9993 100644
--- a/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/SemanticsL1.kt
+++ b/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/SemanticsL1.kt
@@ -25,6 +25,7 @@
 import androidx.ui.layout.Column
 import androidx.ui.layout.Container
 import androidx.ui.layout.CrossAxisAlignment
+import androidx.ui.layout.LayoutSize
 import androidx.ui.layout.MainAxisAlignment
 import androidx.ui.material.Button
 import androidx.ui.material.MaterialTheme
@@ -167,7 +168,7 @@
     actions: Set<SemanticAction<out Any?>> = setOf(),
     children: @Composable() () -> Unit
 ) {
-    Column {
+    Column(mainAxisSize = LayoutSize.Expand) {
         MaterialTheme {
             Collapsable {
                 InvokeActionsByType(actions)
@@ -177,6 +178,7 @@
             }
         }
         Row(
+            mainAxisSize = LayoutSize.Expand,
             mainAxisAlignment = MainAxisAlignment.Center,
             crossAxisAlignment = CrossAxisAlignment.Center
         ) {
@@ -196,7 +198,10 @@
     val secondary =
         actions.firstOrNull { it.types.contains(AccessibilityAction.Secondary) }
     Text(text = "Accessibility Actions By Type", style = +themeTextStyle { h6.copy() })
-    Row(mainAxisAlignment = MainAxisAlignment.SpaceEvenly) {
+    Row(
+        mainAxisSize = LayoutSize.Expand,
+        mainAxisAlignment = MainAxisAlignment.SpaceEvenly
+    ) {
         Button(
             text = "Primary",
              primary?.invoke(ActionCaller.Accessibility) })
@@ -214,7 +219,10 @@
     Text(
         text = "Accessibility Actions By Phrase",
         style = +themeTextStyle { h6.copy() })
-    Row(mainAxisAlignment = MainAxisAlignment.SpaceEvenly) {
+    Row(
+        mainAxisSize = LayoutSize.Expand,
+        mainAxisAlignment = MainAxisAlignment.SpaceEvenly
+    ) {
         actions.forEach {
             Button(
                 text = it.phrase,
@@ -231,7 +239,10 @@
     val positive = actions.firstOrNull { it.types.contains(PolarityAction.Positive) }
     val negative = actions.firstOrNull { it.types.contains(PolarityAction.Negative) }
     Text(text = "Assistant Actions", style = +themeTextStyle { h6.copy() })
-    Row(mainAxisAlignment = MainAxisAlignment.SpaceEvenly) {
+    Row(
+        mainAxisSize = LayoutSize.Expand,
+        mainAxisAlignment = MainAxisAlignment.SpaceEvenly
+    ) {
         Button(
             text = "Negative",
              negative?.invoke(ActionCaller.Assistant) })
@@ -255,7 +266,10 @@
     val unitAction =
         actions.firstOrNull { it.defaultParam is Unit } as SemanticAction<Unit>?
     Text(text = "Actions using Parameters", style = +themeTextStyle { h6.copy() })
-    Row(mainAxisAlignment = MainAxisAlignment.SpaceEvenly) {
+    Row(
+        mainAxisSize = LayoutSize.Expand,
+        mainAxisAlignment = MainAxisAlignment.SpaceEvenly
+    ) {
         Button(
             text = "IntAction",
              pxPositionAction?.invoke(param = PxPosition(1.px, 1.px)) })
@@ -279,7 +293,10 @@
 
     val collapsedState = +state { CollapseMode.Collapsed }
 
-    Row(mainAxisAlignment = MainAxisAlignment.SpaceEvenly) {
+    Row(
+        mainAxisSize = LayoutSize.Expand,
+        mainAxisAlignment = MainAxisAlignment.SpaceEvenly
+    ) {
         Button(text = "Show/Hide Actions", >
             collapsedState.value = when (collapsedState.value) {
                 CollapseMode.Collapsed -> CollapseMode.Visible
diff --git a/ui/ui-layout/api/0.1.0-dev01.txt b/ui/ui-layout/api/0.1.0-dev01.txt
index 0541a98..8d99286 100644
--- a/ui/ui-layout/api/0.1.0-dev01.txt
+++ b/ui/ui-layout/api/0.1.0-dev01.txt
@@ -108,10 +108,10 @@
 
   public final class FlexKt {
     ctor public FlexKt();
-    method public static void Column(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+    method public static void Column(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Wrap, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function0<kotlin.Unit> block);
     method public static void FlexColumn(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function1<? super androidx.ui.layout.FlexChildren,kotlin.Unit> block);
     method public static void FlexRow(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function1<? super androidx.ui.layout.FlexChildren,kotlin.Unit> block);
-    method public static void Row(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+    method public static void Row(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Wrap, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function0<kotlin.Unit> block);
   }
 
   public enum FlowCrossAxisAlignment {
@@ -122,8 +122,8 @@
 
   public final class FlowKt {
     ctor public FlowKt();
-    method public static void FlowColumn(androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.MainAxisAlignment mainAxisAlignment = FlowMainAxisAlignment.Start, androidx.ui.core.Dp mainAxisSpacing = 0.dp, androidx.ui.layout.FlowCrossAxisAlignment crossAxisAlignment = FlowCrossAxisAlignment.Start, androidx.ui.core.Dp crossAxisSpacing = 0.dp, kotlin.jvm.functions.Function0<kotlin.Unit> children);
-    method public static void FlowRow(androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.MainAxisAlignment mainAxisAlignment = FlowMainAxisAlignment.Start, androidx.ui.core.Dp mainAxisSpacing = 0.dp, androidx.ui.layout.FlowCrossAxisAlignment crossAxisAlignment = FlowCrossAxisAlignment.Start, androidx.ui.core.Dp crossAxisSpacing = 0.dp, kotlin.jvm.functions.Function0<kotlin.Unit> children);
+    method public static void FlowColumn(androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Wrap, androidx.ui.layout.MainAxisAlignment mainAxisAlignment = FlowMainAxisAlignment.Start, androidx.ui.core.Dp mainAxisSpacing = 0.dp, androidx.ui.layout.FlowCrossAxisAlignment crossAxisAlignment = FlowCrossAxisAlignment.Start, androidx.ui.core.Dp crossAxisSpacing = 0.dp, kotlin.jvm.functions.Function0<kotlin.Unit> children);
+    method public static void FlowRow(androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Wrap, androidx.ui.layout.MainAxisAlignment mainAxisAlignment = FlowMainAxisAlignment.Start, androidx.ui.core.Dp mainAxisSpacing = 0.dp, androidx.ui.layout.FlowCrossAxisAlignment crossAxisAlignment = FlowCrossAxisAlignment.Start, androidx.ui.core.Dp crossAxisSpacing = 0.dp, kotlin.jvm.functions.Function0<kotlin.Unit> children);
   }
 
   public final class IntrinsicKt {
diff --git a/ui/ui-layout/api/current.txt b/ui/ui-layout/api/current.txt
index 0541a98..8d99286 100644
--- a/ui/ui-layout/api/current.txt
+++ b/ui/ui-layout/api/current.txt
@@ -108,10 +108,10 @@
 
   public final class FlexKt {
     ctor public FlexKt();
-    method public static void Column(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+    method public static void Column(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Wrap, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function0<kotlin.Unit> block);
     method public static void FlexColumn(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function1<? super androidx.ui.layout.FlexChildren,kotlin.Unit> block);
     method public static void FlexRow(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function1<? super androidx.ui.layout.FlexChildren,kotlin.Unit> block);
-    method public static void Row(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+    method public static void Row(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Wrap, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function0<kotlin.Unit> block);
   }
 
   public enum FlowCrossAxisAlignment {
@@ -122,8 +122,8 @@
 
   public final class FlowKt {
     ctor public FlowKt();
-    method public static void FlowColumn(androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.MainAxisAlignment mainAxisAlignment = FlowMainAxisAlignment.Start, androidx.ui.core.Dp mainAxisSpacing = 0.dp, androidx.ui.layout.FlowCrossAxisAlignment crossAxisAlignment = FlowCrossAxisAlignment.Start, androidx.ui.core.Dp crossAxisSpacing = 0.dp, kotlin.jvm.functions.Function0<kotlin.Unit> children);
-    method public static void FlowRow(androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.MainAxisAlignment mainAxisAlignment = FlowMainAxisAlignment.Start, androidx.ui.core.Dp mainAxisSpacing = 0.dp, androidx.ui.layout.FlowCrossAxisAlignment crossAxisAlignment = FlowCrossAxisAlignment.Start, androidx.ui.core.Dp crossAxisSpacing = 0.dp, kotlin.jvm.functions.Function0<kotlin.Unit> children);
+    method public static void FlowColumn(androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Wrap, androidx.ui.layout.MainAxisAlignment mainAxisAlignment = FlowMainAxisAlignment.Start, androidx.ui.core.Dp mainAxisSpacing = 0.dp, androidx.ui.layout.FlowCrossAxisAlignment crossAxisAlignment = FlowCrossAxisAlignment.Start, androidx.ui.core.Dp crossAxisSpacing = 0.dp, kotlin.jvm.functions.Function0<kotlin.Unit> children);
+    method public static void FlowRow(androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Wrap, androidx.ui.layout.MainAxisAlignment mainAxisAlignment = FlowMainAxisAlignment.Start, androidx.ui.core.Dp mainAxisSpacing = 0.dp, androidx.ui.layout.FlowCrossAxisAlignment crossAxisAlignment = FlowCrossAxisAlignment.Start, androidx.ui.core.Dp crossAxisSpacing = 0.dp, kotlin.jvm.functions.Function0<kotlin.Unit> children);
   }
 
   public final class IntrinsicKt {
diff --git a/ui/ui-layout/api/public_plus_experimental_0.1.0-dev01.txt b/ui/ui-layout/api/public_plus_experimental_0.1.0-dev01.txt
index 0541a98..8d99286 100644
--- a/ui/ui-layout/api/public_plus_experimental_0.1.0-dev01.txt
+++ b/ui/ui-layout/api/public_plus_experimental_0.1.0-dev01.txt
@@ -108,10 +108,10 @@
 
   public final class FlexKt {
     ctor public FlexKt();
-    method public static void Column(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+    method public static void Column(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Wrap, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function0<kotlin.Unit> block);
     method public static void FlexColumn(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function1<? super androidx.ui.layout.FlexChildren,kotlin.Unit> block);
     method public static void FlexRow(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function1<? super androidx.ui.layout.FlexChildren,kotlin.Unit> block);
-    method public static void Row(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+    method public static void Row(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Wrap, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function0<kotlin.Unit> block);
   }
 
   public enum FlowCrossAxisAlignment {
@@ -122,8 +122,8 @@
 
   public final class FlowKt {
     ctor public FlowKt();
-    method public static void FlowColumn(androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.MainAxisAlignment mainAxisAlignment = FlowMainAxisAlignment.Start, androidx.ui.core.Dp mainAxisSpacing = 0.dp, androidx.ui.layout.FlowCrossAxisAlignment crossAxisAlignment = FlowCrossAxisAlignment.Start, androidx.ui.core.Dp crossAxisSpacing = 0.dp, kotlin.jvm.functions.Function0<kotlin.Unit> children);
-    method public static void FlowRow(androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.MainAxisAlignment mainAxisAlignment = FlowMainAxisAlignment.Start, androidx.ui.core.Dp mainAxisSpacing = 0.dp, androidx.ui.layout.FlowCrossAxisAlignment crossAxisAlignment = FlowCrossAxisAlignment.Start, androidx.ui.core.Dp crossAxisSpacing = 0.dp, kotlin.jvm.functions.Function0<kotlin.Unit> children);
+    method public static void FlowColumn(androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Wrap, androidx.ui.layout.MainAxisAlignment mainAxisAlignment = FlowMainAxisAlignment.Start, androidx.ui.core.Dp mainAxisSpacing = 0.dp, androidx.ui.layout.FlowCrossAxisAlignment crossAxisAlignment = FlowCrossAxisAlignment.Start, androidx.ui.core.Dp crossAxisSpacing = 0.dp, kotlin.jvm.functions.Function0<kotlin.Unit> children);
+    method public static void FlowRow(androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Wrap, androidx.ui.layout.MainAxisAlignment mainAxisAlignment = FlowMainAxisAlignment.Start, androidx.ui.core.Dp mainAxisSpacing = 0.dp, androidx.ui.layout.FlowCrossAxisAlignment crossAxisAlignment = FlowCrossAxisAlignment.Start, androidx.ui.core.Dp crossAxisSpacing = 0.dp, kotlin.jvm.functions.Function0<kotlin.Unit> children);
   }
 
   public final class IntrinsicKt {
diff --git a/ui/ui-layout/api/public_plus_experimental_current.txt b/ui/ui-layout/api/public_plus_experimental_current.txt
index 0541a98..8d99286 100644
--- a/ui/ui-layout/api/public_plus_experimental_current.txt
+++ b/ui/ui-layout/api/public_plus_experimental_current.txt
@@ -108,10 +108,10 @@
 
   public final class FlexKt {
     ctor public FlexKt();
-    method public static void Column(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+    method public static void Column(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Wrap, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function0<kotlin.Unit> block);
     method public static void FlexColumn(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function1<? super androidx.ui.layout.FlexChildren,kotlin.Unit> block);
     method public static void FlexRow(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function1<? super androidx.ui.layout.FlexChildren,kotlin.Unit> block);
-    method public static void Row(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+    method public static void Row(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Wrap, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function0<kotlin.Unit> block);
   }
 
   public enum FlowCrossAxisAlignment {
@@ -122,8 +122,8 @@
 
   public final class FlowKt {
     ctor public FlowKt();
-    method public static void FlowColumn(androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.MainAxisAlignment mainAxisAlignment = FlowMainAxisAlignment.Start, androidx.ui.core.Dp mainAxisSpacing = 0.dp, androidx.ui.layout.FlowCrossAxisAlignment crossAxisAlignment = FlowCrossAxisAlignment.Start, androidx.ui.core.Dp crossAxisSpacing = 0.dp, kotlin.jvm.functions.Function0<kotlin.Unit> children);
-    method public static void FlowRow(androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.MainAxisAlignment mainAxisAlignment = FlowMainAxisAlignment.Start, androidx.ui.core.Dp mainAxisSpacing = 0.dp, androidx.ui.layout.FlowCrossAxisAlignment crossAxisAlignment = FlowCrossAxisAlignment.Start, androidx.ui.core.Dp crossAxisSpacing = 0.dp, kotlin.jvm.functions.Function0<kotlin.Unit> children);
+    method public static void FlowColumn(androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Wrap, androidx.ui.layout.MainAxisAlignment mainAxisAlignment = FlowMainAxisAlignment.Start, androidx.ui.core.Dp mainAxisSpacing = 0.dp, androidx.ui.layout.FlowCrossAxisAlignment crossAxisAlignment = FlowCrossAxisAlignment.Start, androidx.ui.core.Dp crossAxisSpacing = 0.dp, kotlin.jvm.functions.Function0<kotlin.Unit> children);
+    method public static void FlowRow(androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Wrap, androidx.ui.layout.MainAxisAlignment mainAxisAlignment = FlowMainAxisAlignment.Start, androidx.ui.core.Dp mainAxisSpacing = 0.dp, androidx.ui.layout.FlowCrossAxisAlignment crossAxisAlignment = FlowCrossAxisAlignment.Start, androidx.ui.core.Dp crossAxisSpacing = 0.dp, kotlin.jvm.functions.Function0<kotlin.Unit> children);
   }
 
   public final class IntrinsicKt {
diff --git a/ui/ui-layout/api/restricted_0.1.0-dev01.txt b/ui/ui-layout/api/restricted_0.1.0-dev01.txt
index 0541a98..8d99286 100644
--- a/ui/ui-layout/api/restricted_0.1.0-dev01.txt
+++ b/ui/ui-layout/api/restricted_0.1.0-dev01.txt
@@ -108,10 +108,10 @@
 
   public final class FlexKt {
     ctor public FlexKt();
-    method public static void Column(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+    method public static void Column(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Wrap, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function0<kotlin.Unit> block);
     method public static void FlexColumn(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function1<? super androidx.ui.layout.FlexChildren,kotlin.Unit> block);
     method public static void FlexRow(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function1<? super androidx.ui.layout.FlexChildren,kotlin.Unit> block);
-    method public static void Row(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+    method public static void Row(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Wrap, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function0<kotlin.Unit> block);
   }
 
   public enum FlowCrossAxisAlignment {
@@ -122,8 +122,8 @@
 
   public final class FlowKt {
     ctor public FlowKt();
-    method public static void FlowColumn(androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.MainAxisAlignment mainAxisAlignment = FlowMainAxisAlignment.Start, androidx.ui.core.Dp mainAxisSpacing = 0.dp, androidx.ui.layout.FlowCrossAxisAlignment crossAxisAlignment = FlowCrossAxisAlignment.Start, androidx.ui.core.Dp crossAxisSpacing = 0.dp, kotlin.jvm.functions.Function0<kotlin.Unit> children);
-    method public static void FlowRow(androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.MainAxisAlignment mainAxisAlignment = FlowMainAxisAlignment.Start, androidx.ui.core.Dp mainAxisSpacing = 0.dp, androidx.ui.layout.FlowCrossAxisAlignment crossAxisAlignment = FlowCrossAxisAlignment.Start, androidx.ui.core.Dp crossAxisSpacing = 0.dp, kotlin.jvm.functions.Function0<kotlin.Unit> children);
+    method public static void FlowColumn(androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Wrap, androidx.ui.layout.MainAxisAlignment mainAxisAlignment = FlowMainAxisAlignment.Start, androidx.ui.core.Dp mainAxisSpacing = 0.dp, androidx.ui.layout.FlowCrossAxisAlignment crossAxisAlignment = FlowCrossAxisAlignment.Start, androidx.ui.core.Dp crossAxisSpacing = 0.dp, kotlin.jvm.functions.Function0<kotlin.Unit> children);
+    method public static void FlowRow(androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Wrap, androidx.ui.layout.MainAxisAlignment mainAxisAlignment = FlowMainAxisAlignment.Start, androidx.ui.core.Dp mainAxisSpacing = 0.dp, androidx.ui.layout.FlowCrossAxisAlignment crossAxisAlignment = FlowCrossAxisAlignment.Start, androidx.ui.core.Dp crossAxisSpacing = 0.dp, kotlin.jvm.functions.Function0<kotlin.Unit> children);
   }
 
   public final class IntrinsicKt {
diff --git a/ui/ui-layout/api/restricted_current.txt b/ui/ui-layout/api/restricted_current.txt
index 0541a98..8d99286 100644
--- a/ui/ui-layout/api/restricted_current.txt
+++ b/ui/ui-layout/api/restricted_current.txt
@@ -108,10 +108,10 @@
 
   public final class FlexKt {
     ctor public FlexKt();
-    method public static void Column(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+    method public static void Column(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Wrap, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function0<kotlin.Unit> block);
     method public static void FlexColumn(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function1<? super androidx.ui.layout.FlexChildren,kotlin.Unit> block);
     method public static void FlexRow(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function1<? super androidx.ui.layout.FlexChildren,kotlin.Unit> block);
-    method public static void Row(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+    method public static void Row(androidx.ui.layout.MainAxisAlignment mainAxisAlignment = MainAxisAlignment.Start, androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Wrap, androidx.ui.layout.CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.Start, androidx.ui.layout.LayoutSize crossAxisSize = LayoutSize.Wrap, kotlin.jvm.functions.Function0<kotlin.Unit> block);
   }
 
   public enum FlowCrossAxisAlignment {
@@ -122,8 +122,8 @@
 
   public final class FlowKt {
     ctor public FlowKt();
-    method public static void FlowColumn(androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.MainAxisAlignment mainAxisAlignment = FlowMainAxisAlignment.Start, androidx.ui.core.Dp mainAxisSpacing = 0.dp, androidx.ui.layout.FlowCrossAxisAlignment crossAxisAlignment = FlowCrossAxisAlignment.Start, androidx.ui.core.Dp crossAxisSpacing = 0.dp, kotlin.jvm.functions.Function0<kotlin.Unit> children);
-    method public static void FlowRow(androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Expand, androidx.ui.layout.MainAxisAlignment mainAxisAlignment = FlowMainAxisAlignment.Start, androidx.ui.core.Dp mainAxisSpacing = 0.dp, androidx.ui.layout.FlowCrossAxisAlignment crossAxisAlignment = FlowCrossAxisAlignment.Start, androidx.ui.core.Dp crossAxisSpacing = 0.dp, kotlin.jvm.functions.Function0<kotlin.Unit> children);
+    method public static void FlowColumn(androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Wrap, androidx.ui.layout.MainAxisAlignment mainAxisAlignment = FlowMainAxisAlignment.Start, androidx.ui.core.Dp mainAxisSpacing = 0.dp, androidx.ui.layout.FlowCrossAxisAlignment crossAxisAlignment = FlowCrossAxisAlignment.Start, androidx.ui.core.Dp crossAxisSpacing = 0.dp, kotlin.jvm.functions.Function0<kotlin.Unit> children);
+    method public static void FlowRow(androidx.ui.layout.LayoutSize mainAxisSize = LayoutSize.Wrap, androidx.ui.layout.MainAxisAlignment mainAxisAlignment = FlowMainAxisAlignment.Start, androidx.ui.core.Dp mainAxisSpacing = 0.dp, androidx.ui.layout.FlowCrossAxisAlignment crossAxisAlignment = FlowCrossAxisAlignment.Start, androidx.ui.core.Dp crossAxisSpacing = 0.dp, kotlin.jvm.functions.Function0<kotlin.Unit> children);
   }
 
   public final class IntrinsicKt {
diff --git a/ui/ui-layout/integration-tests/layout-demos/src/main/java/androidx/ui/layout/demos/ComplexLayoutDemos.kt b/ui/ui-layout/integration-tests/layout-demos/src/main/java/androidx/ui/layout/demos/ComplexLayoutDemos.kt
index 7ce5495..05e04c4 100644
--- a/ui/ui-layout/integration-tests/layout-demos/src/main/java/androidx/ui/layout/demos/ComplexLayoutDemos.kt
+++ b/ui/ui-layout/integration-tests/layout-demos/src/main/java/androidx/ui/layout/demos/ComplexLayoutDemos.kt
@@ -397,7 +397,7 @@
 fun RowBaselineAlignment() {
     Row(crossAxisAlignment = CrossAxisAlignment.AlignmentLine(FirstBaseline)) {
         Text("First text")
-        Column(mainAxisSize = LayoutSize.Wrap) {
+        Column {
             SizedRectangle(Color.Blue, width = 10.dp, height = 50.dp)
             Padding(30.dp) {
                 Text("Second text", style = TextStyle(fontSize = 45.sp))
diff --git a/ui/ui-layout/integration-tests/layout-demos/src/main/java/androidx/ui/layout/demos/LayoutActivity.kt b/ui/ui-layout/integration-tests/layout-demos/src/main/java/androidx/ui/layout/demos/LayoutActivity.kt
index 97c8d8e..d841d57 100644
--- a/ui/ui-layout/integration-tests/layout-demos/src/main/java/androidx/ui/layout/demos/LayoutActivity.kt
+++ b/ui/ui-layout/integration-tests/layout-demos/src/main/java/androidx/ui/layout/demos/LayoutActivity.kt
@@ -25,6 +25,7 @@
 import androidx.ui.layout.Container
 import androidx.ui.layout.CrossAxisAlignment
 import androidx.ui.layout.HeightSpacer
+import androidx.ui.layout.LayoutSize
 import androidx.ui.layout.MainAxisAlignment
 import androidx.ui.layout.Row
 import androidx.ui.layout.WidthSpacer
@@ -66,77 +67,102 @@
 fun LayoutDemo() {
     val lightGrey = Color(0xFFCFD8DC)
     Column(
+        mainAxisSize = LayoutSize.Expand,
         mainAxisAlignment = MainAxisAlignment.Start,
         crossAxisAlignment = CrossAxisAlignment.Start
     ) {
         Text(text = "Row", style = TextStyle(fontSize = 48.sp))
         ContainerWithBackground(width = ExampleSize, color = lightGrey) {
-            Row {
+            Row(mainAxisSize = LayoutSize.Expand) {
                 PurpleSquare()
                 CyanSquare()
             }
         }
         HeightSpacer(height = 24.dp)
         ContainerWithBackground(width = ExampleSize, color = lightGrey) {
-            Row(mainAxisAlignment = MainAxisAlignment.Center) {
+            Row(
+                mainAxisSize = LayoutSize.Expand,
+                mainAxisAlignment = MainAxisAlignment.Center
+            ) {
                 PurpleSquare()
                 CyanSquare()
             }
         }
         HeightSpacer(height = 24.dp)
         ContainerWithBackground(width = ExampleSize, color = lightGrey) {
-            Row(mainAxisAlignment = MainAxisAlignment.End) {
+            Row(
+                mainAxisSize = LayoutSize.Expand,
+                mainAxisAlignment = MainAxisAlignment.End
+            ) {
                 PurpleSquare()
                 CyanSquare()
             }
         }
         HeightSpacer(height = 24.dp)
         ContainerWithBackground(width = ExampleSize, color = lightGrey) {
-            Row(crossAxisAlignment = CrossAxisAlignment.Start) {
+            Row(
+                mainAxisSize = LayoutSize.Expand,
+                crossAxisAlignment = CrossAxisAlignment.Start
+            ) {
                 PurpleSquare()
                 CyanSquare()
             }
         }
         HeightSpacer(height = 24.dp)
         ContainerWithBackground(width = ExampleSize, color = lightGrey) {
-            Row(crossAxisAlignment = CrossAxisAlignment.End) {
+            Row(
+                mainAxisSize = LayoutSize.Expand,
+                crossAxisAlignment = CrossAxisAlignment.End
+            ) {
                 PurpleSquare()
                 CyanSquare()
             }
         }
         HeightSpacer(height = 24.dp)
         Text(text = "Column", style = TextStyle(fontSize = 48.sp))
-        Row {
+        Row(mainAxisSize = LayoutSize.Expand) {
             ContainerWithBackground(height = ExampleSize, color = lightGrey) {
-                Column {
+                Column(mainAxisSize = LayoutSize.Expand) {
                     PurpleSquare()
                     CyanSquare()
                 }
             }
             WidthSpacer(width = 24.dp)
             ContainerWithBackground(height = ExampleSize, color = lightGrey) {
-                Column(mainAxisAlignment = MainAxisAlignment.Center) {
+                Column(
+                    mainAxisSize = LayoutSize.Expand,
+                    mainAxisAlignment = MainAxisAlignment.Center
+                ) {
                     PurpleSquare()
                     CyanSquare()
                 }
             }
             WidthSpacer(width = 24.dp)
             ContainerWithBackground(height = ExampleSize, color = lightGrey) {
-                Column(mainAxisAlignment = MainAxisAlignment.End) {
+                Column(
+                    mainAxisSize = LayoutSize.Expand,
+                    mainAxisAlignment = MainAxisAlignment.End
+                ) {
                     PurpleSquare()
                     CyanSquare()
                 }
             }
             WidthSpacer(width = 24.dp)
             ContainerWithBackground(height = ExampleSize, color = lightGrey) {
-                Column(crossAxisAlignment = CrossAxisAlignment.Start) {
+                Column(
+                    mainAxisSize = LayoutSize.Expand,
+                    crossAxisAlignment = CrossAxisAlignment.Start
+                ) {
                     PurpleSquare()
                     CyanSquare()
                 }
             }
             WidthSpacer(width = 24.dp)
             ContainerWithBackground(height = ExampleSize, color = lightGrey) {
-                Column(crossAxisAlignment = CrossAxisAlignment.End) {
+                Column(
+                    mainAxisSize = LayoutSize.Expand,
+                    crossAxisAlignment = CrossAxisAlignment.End
+                ) {
                     PurpleSquare()
                     CyanSquare()
                 }
diff --git a/ui/ui-layout/integration-tests/samples/src/main/java/androidx/ui/layout/samples/IntrinsicSamples.kt b/ui/ui-layout/integration-tests/samples/src/main/java/androidx/ui/layout/samples/IntrinsicSamples.kt
index 8059330..e5dfa8e 100644
--- a/ui/ui-layout/integration-tests/samples/src/main/java/androidx/ui/layout/samples/IntrinsicSamples.kt
+++ b/ui/ui-layout/integration-tests/samples/src/main/java/androidx/ui/layout/samples/IntrinsicSamples.kt
@@ -31,6 +31,7 @@
 import androidx.ui.layout.CrossAxisAlignment
 import androidx.ui.layout.DpConstraints
 import androidx.ui.layout.FlexRow
+import androidx.ui.layout.LayoutSize
 import androidx.ui.layout.MaxIntrinsicHeight
 import androidx.ui.layout.MaxIntrinsicWidth
 import androidx.ui.layout.MinIntrinsicHeight
@@ -51,7 +52,10 @@
 fun SameWidthBoxes() {
     Wrap {
         MinIntrinsicWidth {
-            Column(crossAxisAlignment = CrossAxisAlignment.Stretch) {
+            Column(
+                mainAxisSize = LayoutSize.Expand,
+                crossAxisAlignment = CrossAxisAlignment.Stretch
+            ) {
                 ConstrainedBox(DpConstraints.tightConstraints(width = 20.dp, height = 10.dp)) {
                     DrawShape(RectangleShape, Color.Gray)
                 }
@@ -111,7 +115,10 @@
 fun SameWidthTextBoxes() {
     Wrap {
         MaxIntrinsicWidth {
-            Column(crossAxisAlignment = CrossAxisAlignment.Stretch) {
+            Column(
+                mainAxisSize = LayoutSize.Expand,
+                crossAxisAlignment = CrossAxisAlignment.Stretch
+            ) {
                 Wrap {
                     DrawShape(RectangleShape, Color.Gray)
                     Text("Short text")
diff --git a/ui/ui-layout/src/androidTest/java/androidx/ui/layout/test/FlexTest.kt b/ui/ui-layout/src/androidTest/java/androidx/ui/layout/test/FlexTest.kt
index ea2d8c5..5e99116 100644
--- a/ui/ui-layout/src/androidTest/java/androidx/ui/layout/test/FlexTest.kt
+++ b/ui/ui-layout/src/androidTest/java/androidx/ui/layout/test/FlexTest.kt
@@ -42,6 +42,7 @@
 import androidx.ui.core.OnPositioned
 import androidx.ui.core.VerticalAlignmentLine
 import androidx.ui.core.min
+import androidx.ui.layout.Align
 import androidx.ui.layout.Center
 import androidx.ui.layout.Column
 import androidx.ui.layout.Container
@@ -356,7 +357,7 @@
         val childSize = arrayOf(PxSize(-1.px, -1.px), PxSize(-1.px, -1.px))
         val childPosition = arrayOf(PxPosition(-1.px, -1.px), PxPosition(-1.px, -1.px))
         show {
-            Center {
+            Align(Alignment.CenterLeft) {
                 Row(crossAxisAlignment = CrossAxisAlignment.Start) {
                     Container(width = sizeDp, height = sizeDp) {
                         OnPositioned( coordinates ->
@@ -405,7 +406,7 @@
         val childSize = arrayOf(PxSize(-1.px, -1.px), PxSize(-1.px, -1.px))
         val childPosition = arrayOf(PxPosition(-1.px, -1.px), PxPosition(-1.px, -1.px))
         show {
-            Center {
+            Align(Alignment.CenterLeft) {
                 Row(crossAxisAlignment = CrossAxisAlignment.End) {
                     Container(width = sizeDp, height = sizeDp) {
                         OnPositioned( coordinates ->
@@ -457,7 +458,7 @@
         val childSize = arrayOf(PxSize(-1.px, -1.px), PxSize(-1.px, -1.px))
         val childPosition = arrayOf(PxPosition(-1.px, -1.px), PxPosition(-1.px, -1.px))
         show {
-            Center {
+            Align(Alignment.CenterLeft) {
                 Row(crossAxisAlignment = CrossAxisAlignment.Stretch) {
                     Container(width = sizeDp, height = sizeDp) {
                         OnPositioned( coordinates ->
@@ -500,7 +501,7 @@
         val childSize = arrayOf(PxSize(-1.px, -1.px), PxSize(-1.px, -1.px))
         val childPosition = arrayOf(PxPosition(-1.px, -1.px), PxPosition(-1.px, -1.px))
         show {
-            Center {
+            Align(Alignment.TopCenter) {
                 Column(crossAxisAlignment = CrossAxisAlignment.Start) {
                     Container(width = sizeDp, height = sizeDp) {
                         OnPositioned( coordinates ->
@@ -549,7 +550,7 @@
         val childSize = arrayOf(PxSize(-1.px, -1.px), PxSize(-1.px, -1.px))
         val childPosition = arrayOf(PxPosition(-1.px, -1.px), PxPosition(-1.px, -1.px))
         show {
-            Center {
+            Align(Alignment.TopCenter) {
                 Column(crossAxisAlignment = CrossAxisAlignment.End) {
                     Container(width = sizeDp, height = sizeDp) {
                         OnPositioned( coordinates ->
@@ -602,7 +603,7 @@
         val childSize = arrayOf(PxSize(-1.px, -1.px), PxSize(-1.px, -1.px))
         val childPosition = arrayOf(PxPosition(-1.px, -1.px), PxPosition(-1.px, -1.px))
         show {
-            Center {
+            Align(Alignment.TopCenter) {
                 Column(crossAxisAlignment = CrossAxisAlignment.Stretch) {
                     Container(width = sizeDp, height = sizeDp) {
                         OnPositioned( coordinates ->
@@ -1246,7 +1247,10 @@
         val childLayoutCoordinates = arrayOfNulls<LayoutCoordinates?>(childPosition.size)
         show {
             Center {
-                Row(mainAxisAlignment = MainAxisAlignment.Start) {
+                Row(
+                    mainAxisSize = LayoutSize.Expand,
+                    mainAxisAlignment = MainAxisAlignment.Start
+                ) {
                     for (i in 0 until childPosition.size) {
                         Container(width = sizeDp, height = sizeDp) {
                             OnPositioned( coordinates ->
@@ -1287,7 +1291,10 @@
         val childLayoutCoordinates = arrayOfNulls<LayoutCoordinates?>(childPosition.size)
         show {
             Center {
-                Row(mainAxisAlignment = MainAxisAlignment.End) {
+                Row(
+                    mainAxisSize = LayoutSize.Expand,
+                    mainAxisAlignment = MainAxisAlignment.End
+                ) {
                     for (i in 0 until childPosition.size) {
                         Container(width = sizeDp, height = sizeDp) {
                             OnPositioned( coordinates ->
@@ -1328,7 +1335,10 @@
         val childLayoutCoordinates = arrayOfNulls<LayoutCoordinates?>(childPosition.size)
         show {
             Center {
-                Row(mainAxisAlignment = MainAxisAlignment.Center) {
+                Row(
+                    mainAxisSize = LayoutSize.Expand,
+                    mainAxisAlignment = MainAxisAlignment.Center
+                ) {
                     for (i in 0 until childPosition.size) {
                         Container(width = sizeDp, height = sizeDp) {
                             OnPositioned( coordinates ->
@@ -1370,7 +1380,10 @@
         val childLayoutCoordinates = arrayOfNulls<LayoutCoordinates?>(childPosition.size)
         show {
             Center {
-                Row(mainAxisAlignment = MainAxisAlignment.SpaceEvenly) {
+                Row(
+                    mainAxisSize = LayoutSize.Expand,
+                    mainAxisAlignment = MainAxisAlignment.SpaceEvenly
+                ) {
                     for (i in 0 until childPosition.size) {
                         Container(width = sizeDp, height = sizeDp) {
                             OnPositioned( coordinates ->
@@ -1412,7 +1425,10 @@
         val childLayoutCoordinates = arrayOfNulls<LayoutCoordinates?>(childPosition.size)
         show {
             Center {
-                Row(mainAxisAlignment = MainAxisAlignment.SpaceBetween) {
+                Row(
+                    mainAxisSize = LayoutSize.Expand,
+                    mainAxisAlignment = MainAxisAlignment.SpaceBetween
+                ) {
                     for (i in 0 until childPosition.size) {
                         Container(width = sizeDp, height = sizeDp) {
                             OnPositioned( coordinates ->
@@ -1454,7 +1470,10 @@
         val childLayoutCoordinates = arrayOfNulls<LayoutCoordinates?>(childPosition.size)
         show {
             Center {
-                Row(mainAxisAlignment = MainAxisAlignment.SpaceAround) {
+                Row(
+                    mainAxisSize = LayoutSize.Expand,
+                    mainAxisAlignment = MainAxisAlignment.SpaceAround
+                ) {
                     for (i in 0 until childPosition.size) {
                         Container(width = sizeDp, height = sizeDp) {
                             OnPositioned( coordinates ->
@@ -1496,7 +1515,10 @@
         val childLayoutCoordinates = arrayOfNulls<LayoutCoordinates?>(childPosition.size)
         show {
             Center {
-                Column(mainAxisAlignment = MainAxisAlignment.Start) {
+                Column(
+                    mainAxisSize = LayoutSize.Expand,
+                    mainAxisAlignment = MainAxisAlignment.Start
+                ) {
                     for (i in 0 until childPosition.size) {
                         Container(width = sizeDp, height = sizeDp) {
                             OnPositioned( coordinates ->
@@ -1537,7 +1559,10 @@
         val childLayoutCoordinates = arrayOfNulls<LayoutCoordinates?>(childPosition.size)
         show {
             Center {
-                Column(mainAxisAlignment = MainAxisAlignment.End) {
+                Column(
+                    mainAxisSize = LayoutSize.Expand,
+                    mainAxisAlignment = MainAxisAlignment.End
+                ) {
                     for (i in 0 until childPosition.size) {
                         Container(width = sizeDp, height = sizeDp) {
                             OnPositioned( coordinates ->
@@ -1578,7 +1603,10 @@
         val childLayoutCoordinates = arrayOfNulls<LayoutCoordinates?>(childPosition.size)
         show {
             Center {
-                Column(mainAxisAlignment = MainAxisAlignment.Center) {
+                Column(
+                    mainAxisSize = LayoutSize.Expand,
+                    mainAxisAlignment = MainAxisAlignment.Center
+                ) {
                     for (i in 0 until childPosition.size) {
                         Container(width = sizeDp, height = sizeDp) {
                             OnPositioned( coordinates ->
@@ -1620,7 +1648,10 @@
         val childLayoutCoordinates = arrayOfNulls<LayoutCoordinates?>(childPosition.size)
         show {
             Center {
-                Column(mainAxisAlignment = MainAxisAlignment.SpaceEvenly) {
+                Column(
+                    mainAxisSize = LayoutSize.Expand,
+                    mainAxisAlignment = MainAxisAlignment.SpaceEvenly
+                ) {
                     for (i in 0 until childPosition.size) {
                         Container(width = sizeDp, height = sizeDp) {
                             OnPositioned( coordinates ->
@@ -1662,7 +1693,10 @@
         val childLayoutCoordinates = arrayOfNulls<LayoutCoordinates?>(childPosition.size)
         show {
             Center {
-                Column(mainAxisAlignment = MainAxisAlignment.SpaceBetween) {
+                Column(
+                    mainAxisSize = LayoutSize.Expand,
+                    mainAxisAlignment = MainAxisAlignment.SpaceBetween
+                ) {
                     for (i in 0 until childPosition.size) {
                         Container(width = sizeDp, height = sizeDp) {
                             OnPositioned( coordinates ->
@@ -1704,7 +1738,10 @@
         val childLayoutCoordinates = arrayOfNulls<LayoutCoordinates?>(childPosition.size)
         show {
             Center {
-                Column(mainAxisAlignment = MainAxisAlignment.SpaceAround) {
+                Column(
+                    mainAxisSize = LayoutSize.Expand,
+                    mainAxisAlignment = MainAxisAlignment.SpaceAround
+                ) {
                     for (i in 0 until childPosition.size) {
                         Container(width = sizeDp, height = sizeDp) {
                             OnPositioned( coordinates ->
@@ -1806,12 +1843,13 @@
                 ConstrainedBox(DpConstraints.tightConstraints(50.dp, 40.dp)) { }
             }
         }, @Composable {
-            Row(mainAxisSize = LayoutSize.Wrap) {
+            Row(mainAxisSize = LayoutSize.Expand) {
                 AspectRatio(2f) { }
                 ConstrainedBox(DpConstraints.tightConstraints(50.dp, 40.dp)) { }
             }
         }, @Composable {
             Row(
+                mainAxisSize = LayoutSize.Expand,
                 mainAxisAlignment = MainAxisAlignment.Start,
                 crossAxisAlignment = CrossAxisAlignment.Start
             ) {
@@ -1820,6 +1858,7 @@
             }
         }, @Composable {
             Row(
+                mainAxisSize = LayoutSize.Expand,
                 mainAxisAlignment = MainAxisAlignment.Center,
                 crossAxisAlignment = CrossAxisAlignment.Center
             ) {
@@ -1828,6 +1867,7 @@
             }
         }, @Composable {
             Row(
+                mainAxisSize = LayoutSize.Expand,
                 mainAxisAlignment = MainAxisAlignment.End,
                 crossAxisAlignment = CrossAxisAlignment.End
             ) {
@@ -1836,6 +1876,7 @@
             }
         }, @Composable {
             Row(
+                mainAxisSize = LayoutSize.Expand,
                 mainAxisAlignment = MainAxisAlignment.SpaceAround,
                 crossAxisAlignment = CrossAxisAlignment.Stretch
             ) {
@@ -1843,12 +1884,18 @@
                 ConstrainedBox(DpConstraints.tightConstraints(50.dp, 40.dp)) { }
             }
         }, @Composable {
-            Row(mainAxisAlignment = MainAxisAlignment.SpaceBetween) {
+            Row(
+                mainAxisSize = LayoutSize.Expand,
+                mainAxisAlignment = MainAxisAlignment.SpaceBetween
+            ) {
                 AspectRatio(2f) { }
                 ConstrainedBox(DpConstraints.tightConstraints(50.dp, 40.dp)) { }
             }
         }, @Composable {
-            Row(mainAxisAlignment = MainAxisAlignment.SpaceEvenly) {
+            Row(
+                mainAxisSize = LayoutSize.Expand,
+                mainAxisAlignment = MainAxisAlignment.SpaceEvenly
+            ) {
                 AspectRatio(2f) { }
                 ConstrainedBox(DpConstraints.tightConstraints(50.dp, 40.dp)) { }
             }
@@ -1880,12 +1927,13 @@
                 ConstrainedBox(DpConstraints.tightConstraints(50.dp, 40.dp)) { }
             }
         }, @Composable {
-            Column(mainAxisSize = LayoutSize.Wrap) {
+            Column(mainAxisSize = LayoutSize.Expand) {
                 AspectRatio(2f) { }
                 ConstrainedBox(DpConstraints.tightConstraints(50.dp, 40.dp)) { }
             }
         }, @Composable {
             Column(
+                mainAxisSize = LayoutSize.Expand,
                 mainAxisAlignment = MainAxisAlignment.Start,
                 crossAxisAlignment = CrossAxisAlignment.Start
             ) {
@@ -1894,6 +1942,7 @@
             }
         }, @Composable {
             Column(
+                mainAxisSize = LayoutSize.Expand,
                 mainAxisAlignment = MainAxisAlignment.Center,
                 crossAxisAlignment = CrossAxisAlignment.Center
             ) {
@@ -1902,6 +1951,7 @@
             }
         }, @Composable {
             Column(
+                mainAxisSize = LayoutSize.Expand,
                 mainAxisAlignment = MainAxisAlignment.End,
                 crossAxisAlignment = CrossAxisAlignment.End
             ) {
@@ -1910,6 +1960,7 @@
             }
         }, @Composable {
             Column(
+                mainAxisSize = LayoutSize.Expand,
                 mainAxisAlignment = MainAxisAlignment.SpaceAround,
                 crossAxisAlignment = CrossAxisAlignment.Stretch
             ) {
@@ -1917,12 +1968,18 @@
                 ConstrainedBox(DpConstraints.tightConstraints(50.dp, 40.dp)) { }
             }
         }, @Composable {
-            Column(mainAxisAlignment = MainAxisAlignment.SpaceBetween) {
+            Column(
+                mainAxisSize = LayoutSize.Expand,
+                mainAxisAlignment = MainAxisAlignment.SpaceBetween
+            ) {
                 AspectRatio(2f) { }
                 ConstrainedBox(DpConstraints.tightConstraints(50.dp, 40.dp)) { }
             }
         }, @Composable {
-            Column(mainAxisAlignment = MainAxisAlignment.SpaceEvenly) {
+            Column(
+                mainAxisSize = LayoutSize.Expand,
+                mainAxisAlignment = MainAxisAlignment.SpaceEvenly
+            ) {
                 AspectRatio(2f) { }
                 ConstrainedBox(DpConstraints.tightConstraints(50.dp, 40.dp)) { }
             }
@@ -2318,10 +2375,7 @@
         val layoutLatch = CountDownLatch(4)
         show {
             Wrap {
-                Row(
-                    mainAxisSize = LayoutSize.Wrap,
-                    crossAxisAlignment = CrossAxisAlignment.AlignmentLine(TestAlignmentLine)
-                ) {
+                Row(crossAxisAlignment = CrossAxisAlignment.AlignmentLine(TestAlignmentLine)) {
                     SaveLayoutInfo(rowSize, Ref(), layoutLatch)
                     OnChildPositioned({ coordinates ->
                         childPosition[0].value = coordinates.localToGlobal(PxPosition(0.px, 0.px))
@@ -2360,10 +2414,7 @@
         val layoutLatch = CountDownLatch(4)
         show {
             Wrap {
-                Column(
-                    mainAxisSize = LayoutSize.Wrap,
-                    crossAxisAlignment = CrossAxisAlignment.AlignmentLine(TestAlignmentLine)
-                ) {
+                Column(crossAxisAlignment = CrossAxisAlignment.AlignmentLine(TestAlignmentLine)) {
                     SaveLayoutInfo(columnSize, Ref(), layoutLatch)
                     OnChildPositioned({ coordinates ->
                         childPosition[0].value = coordinates.localToGlobal(PxPosition(0.px, 0.px))
diff --git a/ui/ui-layout/src/androidTest/java/androidx/ui/layout/test/FlowTest.kt b/ui/ui-layout/src/androidTest/java/androidx/ui/layout/test/FlowTest.kt
index 5368563..9ca2b06 100644
--- a/ui/ui-layout/src/androidTest/java/androidx/ui/layout/test/FlowTest.kt
+++ b/ui/ui-layout/src/androidTest/java/androidx/ui/layout/test/FlowTest.kt
@@ -84,7 +84,7 @@
         assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
 
         assertEquals(
-            PxSize(flowWidth, size * 3),
+            PxSize(width = size * 5, height = size * 3),
             flowSize.value
         )
         for (i in 0 until numberOfSquares) {
@@ -138,7 +138,7 @@
         assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
 
         assertEquals(
-            PxSize(size * 5, size * 3),
+            PxSize(width = size * 5, height = size * 3),
             flowSize.value
         )
         for (i in 0 until numberOfSquares) {
@@ -192,7 +192,7 @@
         assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
 
         assertEquals(
-            PxSize(flowWidth, size * 3),
+            PxSize(width = flowWidth, height = size * 3),
             flowSize.value
         )
         for (i in 0 until numberOfSquares) {
@@ -227,7 +227,10 @@
                     positionedLatch.countDown()
                 }) {
                     ConstrainedBox(constraints = DpConstraints(maxWidth = flowWidthDp)) {
-                        FlowRow(mainAxisAlignment = FlowMainAxisAlignment.Center) {
+                        FlowRow(
+                            mainAxisSize = LayoutSize.Expand,
+                            mainAxisAlignment = FlowMainAxisAlignment.Center
+                        ) {
                             for (i in 0 until numberOfSquares) {
                                 Container(width = sizeDp, height = sizeDp) {
                                     SaveLayoutInfo(
@@ -246,7 +249,7 @@
         assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
 
         assertEquals(
-            PxSize(flowWidth, size * 3),
+            PxSize(width = flowWidth, height = size * 3),
             flowSize.value
         )
         for (i in 0 until numberOfSquares) {
@@ -281,7 +284,10 @@
                     positionedLatch.countDown()
                 }) {
                     ConstrainedBox(constraints = DpConstraints(maxWidth = flowWidthDp)) {
-                        FlowRow(mainAxisAlignment = FlowMainAxisAlignment.Start) {
+                        FlowRow(
+                            mainAxisSize = LayoutSize.Expand,
+                            mainAxisAlignment = FlowMainAxisAlignment.Start
+                        ) {
                             for (i in 0 until numberOfSquares) {
                                 Container(width = sizeDp, height = sizeDp) {
                                     SaveLayoutInfo(
@@ -300,7 +306,7 @@
         assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
 
         assertEquals(
-            PxSize(flowWidth, size * 3),
+            PxSize(width = flowWidth, height = size * 3),
             flowSize.value
         )
         for (i in 0 until numberOfSquares) {
@@ -335,7 +341,10 @@
                     positionedLatch.countDown()
                 }) {
                     ConstrainedBox(constraints = DpConstraints(maxWidth = flowWidthDp)) {
-                        FlowRow(mainAxisAlignment = FlowMainAxisAlignment.End) {
+                        FlowRow(
+                            mainAxisSize = LayoutSize.Expand,
+                            mainAxisAlignment = FlowMainAxisAlignment.End
+                        ) {
                             for (i in 0 until numberOfSquares) {
                                 Container(width = sizeDp, height = sizeDp) {
                                     SaveLayoutInfo(
@@ -354,7 +363,7 @@
         assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
 
         assertEquals(
-            PxSize(flowWidth, size * 3),
+            PxSize(width = flowWidth, height = size * 3),
             flowSize.value
         )
         for (i in 0 until numberOfSquares) {
@@ -389,7 +398,10 @@
                     positionedLatch.countDown()
                 }) {
                     ConstrainedBox(constraints = DpConstraints(maxWidth = flowWidthDp)) {
-                        FlowRow(mainAxisAlignment = FlowMainAxisAlignment.SpaceEvenly) {
+                        FlowRow(
+                            mainAxisSize = LayoutSize.Expand,
+                            mainAxisAlignment = FlowMainAxisAlignment.SpaceEvenly
+                        ) {
                             for (i in 0 until numberOfSquares) {
                                 Container(width = sizeDp, height = sizeDp) {
                                     SaveLayoutInfo(
@@ -408,7 +420,7 @@
         assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
 
         assertEquals(
-            PxSize(flowWidth, size * 3),
+            PxSize(width = flowWidth, height = size * 3),
             flowSize.value
         )
         for (i in 0 until numberOfSquares) {
@@ -446,7 +458,10 @@
                     positionedLatch.countDown()
                 }) {
                     ConstrainedBox(constraints = DpConstraints(maxWidth = flowWidthDp)) {
-                        FlowRow(mainAxisAlignment = FlowMainAxisAlignment.SpaceBetween) {
+                        FlowRow(
+                            mainAxisSize = LayoutSize.Expand,
+                            mainAxisAlignment = FlowMainAxisAlignment.SpaceBetween
+                        ) {
                             for (i in 0 until numberOfSquares) {
                                 Container(width = sizeDp, height = sizeDp) {
                                     SaveLayoutInfo(
@@ -465,7 +480,7 @@
         assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
 
         assertEquals(
-            PxSize(flowWidth, size * 3),
+            PxSize(width = flowWidth, height = size * 3),
             flowSize.value
         )
         for (i in 0 until numberOfSquares) {
@@ -503,7 +518,10 @@
                     positionedLatch.countDown()
                 }) {
                     ConstrainedBox(constraints = DpConstraints(maxWidth = flowWidthDp)) {
-                        FlowRow(mainAxisAlignment = FlowMainAxisAlignment.SpaceAround) {
+                        FlowRow(
+                            mainAxisSize = LayoutSize.Expand,
+                            mainAxisAlignment = FlowMainAxisAlignment.SpaceAround
+                        ) {
                             for (i in 0 until numberOfSquares) {
                                 Container(width = sizeDp, height = sizeDp) {
                                     SaveLayoutInfo(
@@ -522,7 +540,7 @@
         assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
 
         assertEquals(
-            PxSize(flowWidth, size * 3),
+            PxSize(width = flowWidth, height = size * 3),
             flowSize.value
         )
         for (i in 0 until numberOfSquares) {
@@ -581,7 +599,7 @@
         assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
 
         assertEquals(
-            PxSize(flowWidth, size * 5),
+            PxSize(width = size * 3 + spacing * 2, height = size * 5),
             flowSize.value
         )
         for (i in 0 until numberOfSquares) {
@@ -638,7 +656,7 @@
         assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
 
         assertEquals(
-            PxSize(flowWidth, size * 6),
+            PxSize(width = size * 5, height = size * 6),
             flowSize.value
         )
         for (i in 0 until numberOfSquares) {
@@ -701,7 +719,7 @@
         assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
 
         assertEquals(
-            PxSize(flowWidth, size * 6),
+            PxSize(width = size * 5, height = size * 6),
             flowSize.value
         )
         for (i in 0 until numberOfSquares) {
@@ -761,7 +779,7 @@
         assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
 
         assertEquals(
-            PxSize(flowWidth, size * 6),
+            PxSize(width = size * 5, height = size * 6),
             flowSize.value
         )
         for (i in 0 until numberOfSquares) {
@@ -823,7 +841,7 @@
         assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
 
         assertEquals(
-            PxSize(flowWidth, size * 3 + spacing * 2),
+            PxSize(width = size * 5, height = size * 3 + spacing * 2),
             flowSize.value
         )
         for (i in 0 until numberOfSquares) {
@@ -877,7 +895,7 @@
         assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
 
         assertEquals(
-            PxSize(size * 3, flowHeight),
+            PxSize(width = size * 3, height = size * 5),
             flowSize.value
         )
         for (i in 0 until numberOfSquares) {
@@ -931,7 +949,7 @@
         assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
 
         assertEquals(
-            PxSize(size * 3, size * 5),
+            PxSize(width = size * 3, height = size * 5),
             flowSize.value
         )
         for (i in 0 until numberOfSquares) {
@@ -985,7 +1003,7 @@
         assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
 
         assertEquals(
-            PxSize(size * 3, flowHeight),
+            PxSize(width = size * 3, height = flowHeight),
             flowSize.value
         )
         for (i in 0 until numberOfSquares) {
@@ -1020,7 +1038,10 @@
                     positionedLatch.countDown()
                 }) {
                     ConstrainedBox(constraints = DpConstraints(maxHeight = flowHeightDp)) {
-                        FlowColumn(mainAxisAlignment = FlowMainAxisAlignment.Center) {
+                        FlowColumn(
+                            mainAxisSize = LayoutSize.Expand,
+                            mainAxisAlignment = FlowMainAxisAlignment.Center
+                        ) {
                             for (i in 0 until numberOfSquares) {
                                 Container(width = sizeDp, height = sizeDp) {
                                     SaveLayoutInfo(
@@ -1039,7 +1060,7 @@
         assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
 
         assertEquals(
-            PxSize(size * 3, flowHeight),
+            PxSize(width = size * 3, height = flowHeight),
             flowSize.value
         )
         for (i in 0 until numberOfSquares) {
@@ -1074,7 +1095,10 @@
                     positionedLatch.countDown()
                 }) {
                     ConstrainedBox(constraints = DpConstraints(maxHeight = flowHeightDp)) {
-                        FlowColumn(mainAxisAlignment = FlowMainAxisAlignment.Start) {
+                        FlowColumn(
+                            mainAxisSize = LayoutSize.Expand,
+                            mainAxisAlignment = FlowMainAxisAlignment.Start
+                        ) {
                             for (i in 0 until numberOfSquares) {
                                 Container(width = sizeDp, height = sizeDp) {
                                     SaveLayoutInfo(
@@ -1093,7 +1117,7 @@
         assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
 
         assertEquals(
-            PxSize(size * 3, flowHeight),
+            PxSize(width = size * 3, height = flowHeight),
             flowSize.value
         )
         for (i in 0 until numberOfSquares) {
@@ -1128,7 +1152,10 @@
                     positionedLatch.countDown()
                 }) {
                     ConstrainedBox(constraints = DpConstraints(maxHeight = flowHeightDp)) {
-                        FlowColumn(mainAxisAlignment = FlowMainAxisAlignment.End) {
+                        FlowColumn(
+                            mainAxisSize = LayoutSize.Expand,
+                            mainAxisAlignment = FlowMainAxisAlignment.End
+                        ) {
                             for (i in 0 until numberOfSquares) {
                                 Container(width = sizeDp, height = sizeDp) {
                                     SaveLayoutInfo(
@@ -1147,7 +1174,7 @@
         assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
 
         assertEquals(
-            PxSize(size * 3, flowHeight),
+            PxSize(width = size * 3, height = flowHeight),
             flowSize.value
         )
         for (i in 0 until numberOfSquares) {
@@ -1182,7 +1209,10 @@
                     positionedLatch.countDown()
                 }) {
                     ConstrainedBox(constraints = DpConstraints(maxHeight = flowHeightDp)) {
-                        FlowColumn(mainAxisAlignment = FlowMainAxisAlignment.SpaceEvenly) {
+                        FlowColumn(
+                            mainAxisSize = LayoutSize.Expand,
+                            mainAxisAlignment = FlowMainAxisAlignment.SpaceEvenly
+                        ) {
                             for (i in 0 until numberOfSquares) {
                                 Container(width = sizeDp, height = sizeDp) {
                                     SaveLayoutInfo(
@@ -1201,7 +1231,7 @@
         assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
 
         assertEquals(
-            PxSize(size * 3, flowHeight),
+            PxSize(width = size * 3, height = flowHeight),
             flowSize.value
         )
         for (i in 0 until numberOfSquares) {
@@ -1239,7 +1269,10 @@
                     positionedLatch.countDown()
                 }) {
                     ConstrainedBox(constraints = DpConstraints(maxHeight = flowHeightDp)) {
-                        FlowColumn(mainAxisAlignment = FlowMainAxisAlignment.SpaceBetween) {
+                        FlowColumn(
+                            mainAxisSize = LayoutSize.Expand,
+                            mainAxisAlignment = FlowMainAxisAlignment.SpaceBetween
+                        ) {
                             for (i in 0 until numberOfSquares) {
                                 Container(width = sizeDp, height = sizeDp) {
                                     SaveLayoutInfo(
@@ -1258,7 +1291,7 @@
         assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
 
         assertEquals(
-            PxSize(size * 3, flowHeight),
+            PxSize(width = size * 3, height = flowHeight),
             flowSize.value
         )
         for (i in 0 until numberOfSquares) {
@@ -1296,7 +1329,10 @@
                     positionedLatch.countDown()
                 }) {
                     ConstrainedBox(constraints = DpConstraints(maxHeight = flowHeightDp)) {
-                        FlowColumn(mainAxisAlignment = FlowMainAxisAlignment.SpaceAround) {
+                        FlowColumn(
+                            mainAxisSize = LayoutSize.Expand,
+                            mainAxisAlignment = FlowMainAxisAlignment.SpaceAround
+                        ) {
                             for (i in 0 until numberOfSquares) {
                                 Container(width = sizeDp, height = sizeDp) {
                                     SaveLayoutInfo(
@@ -1315,7 +1351,7 @@
         assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
 
         assertEquals(
-            PxSize(size * 3, flowHeight),
+            PxSize(width = size * 3, height = flowHeight),
             flowSize.value
         )
         for (i in 0 until numberOfSquares) {
@@ -1374,7 +1410,7 @@
         assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
 
         assertEquals(
-            PxSize(size * 5, flowHeight),
+            PxSize(width = size * 5, height = size * 3 + spacing * 2),
             flowSize.value
         )
         for (i in 0 until numberOfSquares) {
@@ -1431,7 +1467,7 @@
         assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
 
         assertEquals(
-            PxSize(size * 6, flowHeight),
+            PxSize(width = size * 6, height = size * 5),
             flowSize.value
         )
         for (i in 0 until numberOfSquares) {
@@ -1494,7 +1530,7 @@
         assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
 
         assertEquals(
-            PxSize(size * 6, flowHeight),
+            PxSize(width = size * 6, height = size * 5),
             flowSize.value
         )
         for (i in 0 until numberOfSquares) {
@@ -1551,7 +1587,7 @@
         assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
 
         assertEquals(
-            PxSize(size * 6, flowHeight),
+            PxSize(width = size * 6, height = size * 5),
             flowSize.value
         )
         for (i in 0 until numberOfSquares) {
@@ -1613,7 +1649,7 @@
         assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
 
         assertEquals(
-            PxSize(size * 3 + spacing * 2, flowHeight),
+            PxSize(width = size * 3 + spacing * 2, height = size * 5),
             flowSize.value
         )
         for (i in 0 until numberOfSquares) {
diff --git a/ui/ui-layout/src/main/java/androidx/ui/layout/Flex.kt b/ui/ui-layout/src/main/java/androidx/ui/layout/Flex.kt
index b3ece57..6c03fba 100644
--- a/ui/ui-layout/src/main/java/androidx/ui/layout/Flex.kt
+++ b/ui/ui-layout/src/main/java/androidx/ui/layout/Flex.kt
@@ -92,7 +92,7 @@
  * @param mainAxisSize The size of the layout in the main axis dimension.
  * Default is [LayoutSize.Expand].
  * @param crossAxisAlignment The alignment of the layout's children in cross axis direction.
- * Default is [CrossAxisAlignment.Center].
+ * Default is [CrossAxisAlignment.Start].
  * @param crossAxisSize The size of the layout in the cross axis dimension.
  * Default is [LayoutSize.Wrap].
  */
@@ -135,7 +135,7 @@
  * @param mainAxisSize The size of the layout in the main axis dimension.
  * Default is [LayoutSize.Expand].
  * @param crossAxisAlignment The alignment of the layout's children in cross axis direction.
- * Default is [CrossAxisAlignment.Center].
+ * Default is [CrossAxisAlignment.Start].
  * @param crossAxisSize The size of the layout in the cross axis dimension.
  * Default is [LayoutSize.Wrap].
  */
@@ -167,16 +167,16 @@
  * @param mainAxisAlignment The alignment of the layout's children in main axis direction.
  * Default is [MainAxisAlignment.Start].
  * @param mainAxisSize The size of the layout in the main axis dimension.
- * Default is [LayoutSize.Expand].
+ * Default is [LayoutSize.Wrap].
  * @param crossAxisAlignment The alignment of the layout's children in cross axis direction.
- * Default is [CrossAxisAlignment.Center].
+ * Default is [CrossAxisAlignment.Start].
  * @param crossAxisSize The size of the layout in the cross axis dimension.
  * Default is [LayoutSize.Wrap].
  */
 @Composable
 fun Row(
     mainAxisAlignment: MainAxisAlignment = MainAxisAlignment.Start,
-    mainAxisSize: LayoutSize = LayoutSize.Expand,
+    mainAxisSize: LayoutSize = LayoutSize.Wrap,
     crossAxisAlignment: CrossAxisAlignment = CrossAxisAlignment.Start,
     crossAxisSize: LayoutSize = LayoutSize.Wrap,
     block: @Composable() () -> Unit
@@ -203,16 +203,16 @@
  * @param mainAxisAlignment The alignment of the layout's children in main axis direction.
  * Default is [MainAxisAlignment.Start].
  * @param mainAxisSize The size of the layout in the main axis dimension.
- * Default is [LayoutSize.Expand].
+ * Default is [LayoutSize.Wrap].
  * @param crossAxisAlignment The alignment of the layout's children in cross axis direction.
- * Default is [CrossAxisAlignment.Center].
+ * Default is [CrossAxisAlignment.Start].
  * @param crossAxisSize The size of the layout in the cross axis dimension.
  * Default is [LayoutSize.Wrap].
  */
 @Composable
 fun Column(
     mainAxisAlignment: MainAxisAlignment = MainAxisAlignment.Start,
-    mainAxisSize: LayoutSize = LayoutSize.Expand,
+    mainAxisSize: LayoutSize = LayoutSize.Wrap,
     crossAxisAlignment: CrossAxisAlignment = CrossAxisAlignment.Start,
     crossAxisSize: LayoutSize = LayoutSize.Wrap,
     block: @Composable() () -> Unit
@@ -480,10 +480,10 @@
 @Composable
 private fun Flex(
     orientation: LayoutOrientation,
-    mainAxisSize: LayoutSize = LayoutSize.Expand,
-    mainAxisAlignment: MainAxisAlignment = MainAxisAlignment.Start,
-    crossAxisSize: LayoutSize = LayoutSize.Wrap,
-    crossAxisAlignment: CrossAxisAlignment = CrossAxisAlignment.Start,
+    mainAxisSize: LayoutSize,
+    mainAxisAlignment: MainAxisAlignment,
+    crossAxisSize: LayoutSize,
+    crossAxisAlignment: CrossAxisAlignment,
     block: FlexChildren.() -> Unit
 ) {
     fun Placeable.mainAxisSize() =
diff --git a/ui/ui-layout/src/main/java/androidx/ui/layout/Flow.kt b/ui/ui-layout/src/main/java/androidx/ui/layout/Flow.kt
index e290629..0dded70 100644
--- a/ui/ui-layout/src/main/java/androidx/ui/layout/Flow.kt
+++ b/ui/ui-layout/src/main/java/androidx/ui/layout/Flow.kt
@@ -47,7 +47,7 @@
  */
 @Composable
 fun FlowRow(
-    mainAxisSize: LayoutSize = LayoutSize.Expand,
+    mainAxisSize: LayoutSize = LayoutSize.Wrap,
     mainAxisAlignment: FlowMainAxisAlignment = FlowMainAxisAlignment.Start,
     mainAxisSpacing: Dp = 0.dp,
     crossAxisAlignment: FlowCrossAxisAlignment = FlowCrossAxisAlignment.Start,
@@ -83,7 +83,7 @@
  */
 @Composable
 fun FlowColumn(
-    mainAxisSize: LayoutSize = LayoutSize.Expand,
+    mainAxisSize: LayoutSize = LayoutSize.Wrap,
     mainAxisAlignment: FlowMainAxisAlignment = FlowMainAxisAlignment.Start,
     mainAxisSpacing: Dp = 0.dp,
     crossAxisAlignment: FlowCrossAxisAlignment = FlowCrossAxisAlignment.Start,
@@ -127,11 +127,11 @@
 @Composable
 private fun Flow(
     orientation: LayoutOrientation,
-    mainAxisSize: LayoutSize = LayoutSize.Expand,
-    mainAxisAlignment: FlowMainAxisAlignment = FlowMainAxisAlignment.Start,
-    mainAxisSpacing: Dp = 0.dp,
-    crossAxisAlignment: FlowCrossAxisAlignment = FlowCrossAxisAlignment.Start,
-    crossAxisSpacing: Dp = 0.dp,
+    mainAxisSize: LayoutSize,
+    mainAxisAlignment: FlowMainAxisAlignment,
+    mainAxisSpacing: Dp,
+    crossAxisAlignment: FlowCrossAxisAlignment,
+    crossAxisSpacing: Dp,
     children: @Composable() () -> Unit
 ) {
     fun Placeable.mainAxisSize() =
diff --git a/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/AppBarActivity.kt b/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/AppBarActivity.kt
index a94a1cf..950b91b 100644
--- a/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/AppBarActivity.kt
+++ b/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/AppBarActivity.kt
@@ -27,6 +27,7 @@
 import androidx.ui.layout.Container
 import androidx.ui.layout.CrossAxisAlignment
 import androidx.ui.layout.FlexColumn
+import androidx.ui.layout.LayoutSize
 import androidx.ui.layout.MainAxisAlignment
 import androidx.ui.material.RadioGroup
 import androidx.ui.material.demos.AppBarActivity.BottomAppBarOption.CenterFab
@@ -90,6 +91,7 @@
                 }
                 flexible(1f) {
                     Column(
+                        mainAxisSize = LayoutSize.Expand,
                         mainAxisAlignment = MainAxisAlignment.SpaceBetween,
                         crossAxisAlignment = CrossAxisAlignment.Center
                     ) {
diff --git a/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/ButtonActivity.kt b/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/ButtonActivity.kt
index ea37d05..3831189 100644
--- a/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/ButtonActivity.kt
+++ b/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/ButtonActivity.kt
@@ -39,6 +39,7 @@
 import androidx.ui.layout.Center
 import androidx.ui.layout.Column
 import androidx.ui.layout.CrossAxisAlignment
+import androidx.ui.layout.LayoutSize
 import androidx.ui.layout.MainAxisAlignment
 import androidx.ui.material.Button
 import androidx.ui.material.ContainedButtonStyle
@@ -56,6 +57,7 @@
         val onClick: () -> Unit = { Log.e("ButtonDemo", "onClick") }
         Center {
             Column(
+                mainAxisSize = LayoutSize.Expand,
                 mainAxisAlignment = MainAxisAlignment.SpaceEvenly,
                 crossAxisAlignment = CrossAxisAlignment.Center
             ) {
diff --git a/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/FloatingActionButtonActivity.kt b/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/FloatingActionButtonActivity.kt
index b7e20c9..4655eb6 100644
--- a/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/FloatingActionButtonActivity.kt
+++ b/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/FloatingActionButtonActivity.kt
@@ -23,6 +23,7 @@
 import androidx.ui.layout.Center
 import androidx.ui.layout.Column
 import androidx.ui.layout.CrossAxisAlignment
+import androidx.ui.layout.LayoutSize
 import androidx.ui.layout.MainAxisAlignment
 import androidx.ui.material.FloatingActionButton
 
@@ -34,6 +35,7 @@
         Center {
             val onClick: () -> Unit = { Log.e("FABDemo", "onClick") }
             Column(
+                mainAxisSize = LayoutSize.Expand,
                 mainAxisAlignment = MainAxisAlignment.SpaceEvenly,
                 crossAxisAlignment = CrossAxisAlignment.Center
             ) {
diff --git a/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/ProgressIndicatorActivity.kt b/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/ProgressIndicatorActivity.kt
index 2b6aded..30fc9c4 100644
--- a/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/ProgressIndicatorActivity.kt
+++ b/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/ProgressIndicatorActivity.kt
@@ -18,6 +18,7 @@
 
 import android.os.Handler
 import androidx.ui.layout.FlexColumn
+import androidx.ui.layout.LayoutSize
 import androidx.ui.layout.MainAxisAlignment.SpaceEvenly
 import androidx.ui.layout.Row
 import androidx.ui.material.CircularProgressIndicator
@@ -86,12 +87,18 @@
 
     FlexColumn {
         expanded(flex = 1f) {
-            Row(mainAxisAlignment = SpaceEvenly) {
+            Row(
+                mainAxisSize = LayoutSize.Expand,
+                mainAxisAlignment = SpaceEvenly
+            ) {
                 // Determinate indicators
                 LinearProgressIndicator(progress = state.progress)
                 CircularProgressIndicator(progress = state.progress)
             }
-            Row(mainAxisAlignment = SpaceEvenly) {
+            Row(
+                mainAxisSize = LayoutSize.Expand,
+                mainAxisAlignment = SpaceEvenly
+            ) {
                 // Fancy colours!
                 LinearProgressIndicator(progress = (state.progress), color = state.generateColor())
                 CircularProgressIndicator(
@@ -99,7 +106,10 @@
                     color = state.generateColor()
                 )
             }
-            Row(mainAxisAlignment = SpaceEvenly) {
+            Row(
+                mainAxisSize = LayoutSize.Expand,
+                mainAxisAlignment = SpaceEvenly
+            ) {
                 // Indeterminate indicators
                 LinearProgressIndicator()
                 CircularProgressIndicator()
diff --git a/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/SelectionControlsActivity.kt b/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/SelectionControlsActivity.kt
index 9db2df8..cf1e081 100644
--- a/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/SelectionControlsActivity.kt
+++ b/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/SelectionControlsActivity.kt
@@ -28,7 +28,6 @@
 import androidx.ui.layout.Column
 import androidx.ui.layout.CrossAxisAlignment
 import androidx.ui.layout.EdgeInsets
-import androidx.ui.layout.LayoutSize
 import androidx.ui.layout.MainAxisAlignment
 import androidx.ui.layout.Padding
 import androidx.ui.layout.Row
@@ -97,7 +96,7 @@
         val textStyle = +themeTextStyle { subtitle1 }
 
         RadioGroup {
-            Row(mainAxisSize = LayoutSize.Wrap) {
+            Row {
                 radioOptions.forEach { text ->
                     val selected = text == selectedOption
                     RadioGroupItem(
@@ -150,10 +149,7 @@
 
     @Composable
     fun SwitchDemo() {
-        Row(
-            mainAxisAlignment = MainAxisAlignment.SpaceAround,
-            mainAxisSize = LayoutSize.Wrap
-        ) {
+        Row(mainAxisAlignment = MainAxisAlignment.SpaceAround) {
             val (checked, onChecked) = +state { false }
             val (checked2, onChecked2) = +state { false }
             val (checked3, onChecked3) = +state { true }
@@ -167,10 +163,7 @@
 
     @Composable
     fun RadioButtonDemo() {
-        Row(
-            mainAxisAlignment = MainAxisAlignment.SpaceAround,
-            mainAxisSize = LayoutSize.Wrap
-        ) {
+        Row(mainAxisAlignment = MainAxisAlignment.SpaceAround) {
             RadioButton(selected = true, >
             RadioButton(selected = false, >
             RadioButton(selected = true, color = customColor, >
diff --git a/ui/ui-material/integration-tests/material-studies/src/main/java/androidx/ui/material/studies/rally/RallyActivity.kt b/ui/ui-material/integration-tests/material-studies/src/main/java/androidx/ui/material/studies/rally/RallyActivity.kt
index 9d7ac85..5d4d808 100644
--- a/ui/ui-material/integration-tests/material-studies/src/main/java/androidx/ui/material/studies/rally/RallyActivity.kt
+++ b/ui/ui-material/integration-tests/material-studies/src/main/java/androidx/ui/material/studies/rally/RallyActivity.kt
@@ -26,6 +26,7 @@
 import androidx.ui.core.setContent
 import androidx.ui.layout.Column
 import androidx.ui.layout.HeightSpacer
+import androidx.ui.layout.LayoutSize
 import androidx.ui.layout.Padding
 import androidx.ui.layout.Row
 import androidx.ui.material.studies.Scaffold
@@ -55,7 +56,7 @@
     @Composable
     fun RallyAppBar() {
         // TODO: Transform to tabs
-        Row {
+        Row(mainAxisSize = LayoutSize.Expand) {
             // Icon()
             Text(text = "Overview", style = +themeTextStyle{ h4 })
             // TODO: Other items
@@ -66,7 +67,7 @@
 @Composable
 fun RallyBody() {
     Padding(padding = 16.dp) {
-        Column {
+        Column(mainAxisSize = LayoutSize.Expand) {
             // TODO: scrolling container
             RallyAlertCard()
             HeightSpacer(height = 10.dp)
diff --git a/ui/ui-material/integration-tests/material-studies/src/main/java/androidx/ui/material/studies/rally/RallyCards.kt b/ui/ui-material/integration-tests/material-studies/src/main/java/androidx/ui/material/studies/rally/RallyCards.kt
index 9ba52df..ec5dc49 100644
--- a/ui/ui-material/integration-tests/material-studies/src/main/java/androidx/ui/material/studies/rally/RallyCards.kt
+++ b/ui/ui-material/integration-tests/material-studies/src/main/java/androidx/ui/material/studies/rally/RallyCards.kt
@@ -29,6 +29,7 @@
 import androidx.ui.layout.EdgeInsets
 import androidx.ui.layout.FixedSpacer
 import androidx.ui.layout.FlexRow
+import androidx.ui.layout.LayoutSize
 import androidx.ui.layout.MainAxisAlignment
 import androidx.ui.layout.Padding
 import androidx.ui.layout.Row
@@ -50,8 +51,11 @@
 fun RallyAlertCard() {
     Card(color = cardInternalColor) {
         Padding(padding = 12.dp) {
-            Column {
-                Row(mainAxisAlignment = MainAxisAlignment.SpaceBetween) {
+            Column(mainAxisSize = LayoutSize.Expand) {
+                Row(
+                    mainAxisSize = LayoutSize.Expand,
+                    mainAxisAlignment = MainAxisAlignment.SpaceBetween
+                ) {
                     Text(text = "Alerts", style = +themeTextStyle { subtitle2 })
                     Button(text = "See All",  }, style = TextButtonStyle())
                 }
@@ -86,16 +90,16 @@
 @Composable
 fun RallyAccountsCard() {
     Card(color = cardInternalColor) {
-        Column {
+        Column(mainAxisSize = LayoutSize.Expand) {
             Padding(padding = 12.dp) {
-                Column {
+                Column(mainAxisSize = LayoutSize.Expand) {
                     Text(text = "Accounts", style = +themeTextStyle { body1 })
                     Text(text = "$12,132.49", style = +themeTextStyle { h3 })
                 }
             }
             Divider(color = rallyGreen, height = 1.dp)
             Padding(padding = 12.dp) {
-                Column {
+                Column(mainAxisSize = LayoutSize.Expand) {
                     val colors = +ambient(Colors)
                     RallyAccountRow(
                         name = "Checking",
@@ -135,7 +139,10 @@
             inflexible {
                 AccountIndicator(color = color)
                 WidthSpacer(width = 8.dp)
-                Column(crossAxisAlignment = CrossAxisAlignment.Start) {
+                Column(
+                    mainAxisSize = LayoutSize.Expand,
+                    crossAxisAlignment = CrossAxisAlignment.Start
+                ) {
                     Text(text = name, style = +themeTextStyle { body1 })
                     Text(text = "•••••$number", style = +themeTextStyle { subtitle1 })
                 }
@@ -164,9 +171,9 @@
 @Composable
 fun RallyBillsCard() {
     Card(color = cardInternalColor) {
-        Column {
+        Column(mainAxisSize = LayoutSize.Expand) {
             Padding(padding = 12.dp) {
-                Column {
+                Column(mainAxisSize = LayoutSize.Expand) {
                     Text(text = "Bills", style = +themeTextStyle { subtitle2 })
                     Text(text = "$1,810.00", style = +themeTextStyle { h1 })
                 }
diff --git a/ui/ui-material/integration-tests/samples/src/main/java/androidx/ui/material/samples/DrawerSamples.kt b/ui/ui-material/integration-tests/samples/src/main/java/androidx/ui/material/samples/DrawerSamples.kt
index 9572048..b099612 100644
--- a/ui/ui-material/integration-tests/samples/src/main/java/androidx/ui/material/samples/DrawerSamples.kt
+++ b/ui/ui-material/integration-tests/samples/src/main/java/androidx/ui/material/samples/DrawerSamples.kt
@@ -29,6 +29,7 @@
 import androidx.ui.layout.Column
 import androidx.ui.layout.Container
 import androidx.ui.layout.HeightSpacer
+import androidx.ui.layout.LayoutSize
 import androidx.ui.layout.Row
 import androidx.ui.material.BottomDrawerLayout
 import androidx.ui.material.Button
@@ -39,7 +40,7 @@
 @Sampled
 @Composable
 fun StaticDrawerSample() {
-    Row {
+    Row(mainAxisSize = LayoutSize.Expand) {
         StaticDrawer {
             Center {
                 Text("Drawer Content")
@@ -81,7 +82,7 @@
 @Composable
 private fun YourDrawerContent(onStateChange: (DrawerState) -> Unit) {
     Container(expanded = true) {
-        Column {
+        Column(mainAxisSize = LayoutSize.Expand) {
             Text(text = "Drawer Content")
             HeightSpacer(20.dp)
             Button(
@@ -94,7 +95,7 @@
 @Composable
 private fun YourAppContent(text: String, onDrawerStateChange: (DrawerState) -> Unit) {
     Center {
-        Column {
+        Column(mainAxisSize = LayoutSize.Expand) {
             Text(text = text)
             HeightSpacer(20.dp)
             Button(
diff --git a/ui/ui-material/integration-tests/samples/src/main/java/androidx/ui/material/samples/TabSamples.kt b/ui/ui-material/integration-tests/samples/src/main/java/androidx/ui/material/samples/TabSamples.kt
index 3968506..ccc8c6a 100644
--- a/ui/ui-material/integration-tests/samples/src/main/java/androidx/ui/material/samples/TabSamples.kt
+++ b/ui/ui-material/integration-tests/samples/src/main/java/androidx/ui/material/samples/TabSamples.kt
@@ -41,6 +41,7 @@
 import androidx.ui.layout.CrossAxisAlignment
 import androidx.ui.layout.EdgeInsets
 import androidx.ui.layout.FlexColumn
+import androidx.ui.layout.LayoutSize
 import androidx.ui.layout.Padding
 import androidx.ui.material.Tab
 import androidx.ui.material.TabRow
@@ -212,7 +213,10 @@
 fun FancyTab(title: String, onClick: () -> Unit, selected: Boolean) {
     MutuallyExclusiveSetItem(selected = selected,  onClick() }) {
         Container(height = 50.dp, padding = EdgeInsets(10.dp)) {
-            Column(crossAxisAlignment = CrossAxisAlignment.Center) {
+            Column(
+                mainAxisSize = LayoutSize.Expand,
+                crossAxisAlignment = CrossAxisAlignment.Center
+            ) {
                 val color = if (selected) Color.Red else Color.Gray
                 ColoredRect(height = 10.dp, width = 10.dp, color = color)
                 Padding(5.dp) {
diff --git a/ui/ui-material/src/androidTest/java/androidx/ui/material/TabTest.kt b/ui/ui-material/src/androidTest/java/androidx/ui/material/TabTest.kt
index 3bba78c..957cb5c 100644
--- a/ui/ui-material/src/androidTest/java/androidx/ui/material/TabTest.kt
+++ b/ui/ui-material/src/androidTest/java/androidx/ui/material/TabTest.kt
@@ -42,6 +42,7 @@
 import androidx.ui.test.findAll
 import androidx.ui.test.isInMutuallyExclusiveGroup
 import com.google.common.truth.Truth
+import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -103,6 +104,7 @@
             .assertHeightEqualsTo(ExpectedLargeTabHeight)
     }
 
+    @Ignore("b/140292836")
     @Test
     fun tabRow_indicatorPosition() {
         val indicatorHeight = 1.dp
diff --git a/ui/ui-material/src/main/java/androidx/ui/material/AlertDialog.kt b/ui/ui-material/src/main/java/androidx/ui/material/AlertDialog.kt
index fa338c69..0e8f3a3 100644
--- a/ui/ui-material/src/main/java/androidx/ui/material/AlertDialog.kt
+++ b/ui/ui-material/src/main/java/androidx/ui/material/AlertDialog.kt
@@ -81,8 +81,7 @@
         MaterialTheme(colors = currentColors, typography = currentTypography) {
             Surface(shape = AlertDialogShape) {
                 Container(width = AlertDialogWidth) {
-                    Column(mainAxisSize = LayoutSize.Wrap,
-                        crossAxisAlignment = CrossAxisAlignment.Start) {
+                    Column(crossAxisAlignment = CrossAxisAlignment.Start) {
                         if (title != null) {
                             Container(
                                 alignment = Alignment.CenterLeft,
@@ -138,10 +137,7 @@
 ) {
     Container(padding = ButtonsPadding, alignment = Alignment.CenterRight, expanded = true) {
         if (buttonLayout == AlertDialogButtonLayout.SideBySide) {
-            Row(
-                mainAxisAlignment = MainAxisAlignment.End,
-                mainAxisSize = LayoutSize.Wrap
-            ) {
+            Row(mainAxisAlignment = MainAxisAlignment.End) {
                 if (dismissButton != null) {
                     dismissButton()
                     WidthSpacer(ButtonsWidthSpace)
@@ -150,7 +146,7 @@
                 confirmButton()
             }
         } else {
-            Column {
+            Column(mainAxisSize = LayoutSize.Expand) {
                 confirmButton()
 
                 if (dismissButton != null) {
diff --git a/ui/ui-material/src/main/java/androidx/ui/material/AppBar.kt b/ui/ui-material/src/main/java/androidx/ui/material/AppBar.kt
index de75291..7b9e7f2 100644
--- a/ui/ui-material/src/main/java/androidx/ui/material/AppBar.kt
+++ b/ui/ui-material/src/main/java/androidx/ui/material/AppBar.kt
@@ -50,6 +50,7 @@
 import androidx.ui.material.surface.Surface
 import androidx.ui.graphics.Color
 import androidx.ui.layout.EdgeInsets
+import androidx.ui.layout.LayoutSize
 import androidx.ui.layout.Wrap
 import androidx.ui.material.BottomAppBar.FabConfiguration
 import androidx.ui.material.BottomAppBar.FabPosition
@@ -672,7 +673,7 @@
         it.index < actionsToDisplay
     }
 
-    Row {
+    Row(mainAxisSize = LayoutSize.Expand) {
         shownActions.forEach { (index, shownAction) ->
             action(shownAction)
             if (index != shownActions.lastIndex) {
diff --git a/ui/ui-material/src/main/java/androidx/ui/material/FloatingActionButton.kt b/ui/ui-material/src/main/java/androidx/ui/material/FloatingActionButton.kt
index 9467fbd..972e635 100644
--- a/ui/ui-material/src/main/java/androidx/ui/material/FloatingActionButton.kt
+++ b/ui/ui-material/src/main/java/androidx/ui/material/FloatingActionButton.kt
@@ -144,7 +144,7 @@
             }
         } else {
             Padding(left = ExtendedFabIconPadding, right = ExtendedFabTextPadding) {
-                Row(mainAxisSize = LayoutSize.Wrap) {
+                Row {
                     SimpleImage(image = icon)
                     WidthSpacer(width = ExtendedFabIconPadding)
                     Text(text = text, style = textStyle)
diff --git a/ui/ui-material/src/main/java/androidx/ui/material/RadioButton.kt b/ui/ui-material/src/main/java/androidx/ui/material/RadioButton.kt
index 0c6fd3c..85a74d3 100644
--- a/ui/ui-material/src/main/java/androidx/ui/material/RadioButton.kt
+++ b/ui/ui-material/src/main/java/androidx/ui/material/RadioButton.kt
@@ -109,7 +109,7 @@
     textStyle: TextStyle? = null
 ) {
     RadioGroup {
-        Column(mainAxisSize = LayoutSize.Wrap) {
+        Column {
             options.forEach { text ->
                 RadioGroupTextItem(
                     selected = (text == selectedOption),
diff --git a/ui/ui-material/src/main/java/androidx/ui/material/Tab.kt b/ui/ui-material/src/main/java/androidx/ui/material/Tab.kt
index 518db90..bc1dac4 100644
--- a/ui/ui-material/src/main/java/androidx/ui/material/Tab.kt
+++ b/ui/ui-material/src/main/java/androidx/ui/material/Tab.kt
@@ -41,6 +41,7 @@
 import androidx.ui.layout.Container
 import androidx.ui.layout.CrossAxisAlignment
 import androidx.ui.layout.FlexRow
+import androidx.ui.layout.LayoutSize
 import androidx.ui.layout.MainAxisAlignment
 import androidx.ui.layout.Padding
 import androidx.ui.layout.Stack
@@ -363,6 +364,7 @@
             Padding(top = SingleRowTextImagePadding, bottom = SingleRowTextBaselinePadding) {
                 TabTransition(color = tint, selected = selected) { tabTintColor ->
                     Column(
+                        mainAxisSize = LayoutSize.Expand,
                         mainAxisAlignment = MainAxisAlignment.SpaceBetween,
                         crossAxisAlignment = CrossAxisAlignment.Center
                     ) {
diff --git a/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/ComposeInputField.kt b/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/ComposeInputField.kt
index f590b27..c20c77b 100644
--- a/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/ComposeInputField.kt
+++ b/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/ComposeInputField.kt
@@ -27,6 +27,7 @@
 import androidx.ui.input.KeyboardType
 import androidx.ui.layout.Column
 import androidx.ui.layout.CrossAxisAlignment
+import androidx.ui.layout.LayoutSize
 import androidx.ui.foundation.VerticalScroller
 import androidx.ui.text.TextStyle
 
@@ -54,7 +55,10 @@
 @Composable
 fun InputFieldDemo() {
     VerticalScroller {
-        Column(crossAxisAlignment = CrossAxisAlignment.Start) {
+        Column(
+            mainAxisSize = LayoutSize.Expand,
+            crossAxisAlignment = CrossAxisAlignment.Start
+        ) {
             TagLine(tag = "simple editing")
             EditLine()
             TagLine(tag = "simple editing2")
diff --git a/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/ComposeInputFieldFocusTransition.kt b/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/ComposeInputFieldFocusTransition.kt
index f76b700..bcbc6cb 100644
--- a/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/ComposeInputFieldFocusTransition.kt
+++ b/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/ComposeInputFieldFocusTransition.kt
@@ -31,12 +31,16 @@
 import androidx.ui.input.EditorStyle
 import androidx.ui.input.ImeAction
 import androidx.ui.layout.Column
+import androidx.ui.layout.LayoutSize
 import androidx.ui.text.TextStyle
 
 @Composable
 fun TextFieldFocusTransition() {
     VerticalScroller {
-        Column(crossAxisAlignment = CrossAxisAlignment.Start) {
+        Column(
+            mainAxisSize = LayoutSize.Expand,
+            crossAxisAlignment = CrossAxisAlignment.Start
+        ) {
             TextFiledWithFocusId("Focus 1", "Focus 2")
             TextFiledWithFocusId("Focus 2", "Focus 3")
             TextFiledWithFocusId("Focus 3", "Focus 4")
diff --git a/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/ComposeInputFieldTrickyUseCase.kt b/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/ComposeInputFieldTrickyUseCase.kt
index 435813b..eb1e6ca 100644
--- a/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/ComposeInputFieldTrickyUseCase.kt
+++ b/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/ComposeInputFieldTrickyUseCase.kt
@@ -24,6 +24,7 @@
 import androidx.ui.core.sp
 import androidx.ui.layout.Column
 import androidx.ui.layout.CrossAxisAlignment
+import androidx.ui.layout.LayoutSize
 import androidx.ui.foundation.VerticalScroller
 import androidx.ui.input.EditorModel
 import androidx.ui.input.EditorStyle
@@ -33,7 +34,10 @@
 @Composable
 fun InputFieldTrickyUseCase() {
     VerticalScroller {
-        Column(crossAxisAlignment = CrossAxisAlignment.Start) {
+        Column(
+            mainAxisSize = LayoutSize.Expand,
+            crossAxisAlignment = CrossAxisAlignment.Start
+        ) {
             TagLine(tag = "don't set if non number is added")
             RejectNonDigits()
 
diff --git a/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/ComposeText.kt b/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/ComposeText.kt
index 1703ca8..4211daf 100644
--- a/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/ComposeText.kt
+++ b/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/ComposeText.kt
@@ -36,6 +36,7 @@
 import androidx.ui.graphics.lerp
 import androidx.ui.layout.Column
 import androidx.ui.layout.CrossAxisAlignment
+import androidx.ui.layout.LayoutSize
 import androidx.ui.layout.Row
 import androidx.ui.foundation.VerticalScroller
 import androidx.ui.text.ParagraphStyle
@@ -61,7 +62,10 @@
 @Composable
 fun TextDemo() {
     VerticalScroller {
-        Column(crossAxisAlignment = CrossAxisAlignment.Start) {
+        Column(
+            mainAxisSize = LayoutSize.Expand,
+            crossAxisAlignment = CrossAxisAlignment.Start
+        ) {
             TagLine(tag = "color, fontSize, fontWeight and fontStyle")
             TextDemoBasic()
             TagLine(tag = "Chinese, Arabic, and Hindi")
@@ -315,7 +319,7 @@
 @Composable
 fun TextDemoHeight() {
     // This group of text widgets show different height.
-    Row {
+    Row(mainAxisSize = LayoutSize.Expand) {
         Text {
             Span(
                 text = "$displayText\n$displayText   ",
@@ -406,7 +410,10 @@
     for (i in 1..10) {
         text = "$text$displayText "
     }
-    Column(crossAxisAlignment = CrossAxisAlignment.Start) {
+    Column(
+        mainAxisSize = LayoutSize.Expand,
+        crossAxisAlignment = CrossAxisAlignment.Start
+    ) {
         SecondTagLine(tag = "textAlign = TextAlign.Left")
         Text(paragraphStyle = ParagraphStyle(textAlign = TextAlign.Left)) {
             Span(text = displayText, style = TextStyle(fontSize = fontSize8))
@@ -475,7 +482,10 @@
     val textStyle =
         TextStyle(fontSize = fontSize8, color = Color(0xFFFF0000))
 
-    Column(crossAxisAlignment = CrossAxisAlignment.Start) {
+    Column(
+        mainAxisSize = LayoutSize.Expand,
+        crossAxisAlignment = CrossAxisAlignment.Start
+    ) {
         Text {
             Span(text = text, style = textStyle)
         }
@@ -610,9 +620,9 @@
     SelectionContainer(
         selection = selection.value,
          selection.value = it }) {
-        Column {
+        Column(mainAxisSize = LayoutSize.Expand) {
             for (i in 0..2) {
-                Row {
+                Row(mainAxisSize = LayoutSize.Expand) {
                     for (j in 0..2) {
                         Text {
                             Span(
@@ -655,9 +665,9 @@
          selection.value = it },
         mode = SelectionMode.Horizontal
     ) {
-        Column {
+        Column(mainAxisSize = LayoutSize.Expand) {
             for (i in 0..2) {
-                Row {
+                Row(mainAxisSize = LayoutSize.Expand) {
                     for (j in 0..2) {
                         Text {
                             Span(
diff --git a/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/ComposeVariousInputField.kt b/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/ComposeVariousInputField.kt
index dab3dae..8ccd251 100644
--- a/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/ComposeVariousInputField.kt
+++ b/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/ComposeVariousInputField.kt
@@ -35,6 +35,7 @@
 import androidx.ui.input.KeyboardType
 import androidx.ui.layout.Column
 import androidx.ui.layout.CrossAxisAlignment
+import androidx.ui.layout.LayoutSize
 import androidx.ui.foundation.VerticalScroller
 import androidx.ui.text.AnnotatedString
 import androidx.ui.text.Locale
@@ -176,7 +177,10 @@
 @Composable
 fun VariousInputFieldDemo() {
     VerticalScroller {
-        Column(crossAxisAlignment = CrossAxisAlignment.Start) {
+        Column(
+            mainAxisSize = LayoutSize.Expand,
+            crossAxisAlignment = CrossAxisAlignment.Start
+        ) {
             TagLine(tag = "Capitalization")
             VariousEditLine(
                 keyboardType = KeyboardType.Ascii,
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/EditTextFocusTest.kt b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/EditTextFocusTest.kt
index ca5f7fa..1910394 100644
--- a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/EditTextFocusTest.kt
+++ b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/EditTextFocusTest.kt
@@ -35,6 +35,7 @@
 import androidx.test.filters.LargeTest
 import androidx.viewpager2.widget.ViewPager2.ORIENTATION_HORIZONTAL
 import org.hamcrest.CoreMatchers.allOf
+import org.junit.Assert.assertFalse
 import org.junit.Test
 import org.junit.runner.RunWith
 import java.util.concurrent.TimeUnit.SECONDS
@@ -81,25 +82,24 @@
         test_notCurrentPage_requestFocus(true)
     }
 
-    /** Verifies we navigate to another page if focus is requested on its element. */
+    /** Verifies we don't navigate to another page if focus is requested on its element. */
     private fun test_notCurrentPage_requestFocus(wrapEditTextInViewGroup: Boolean) {
         val pageCount = 3
         setUpTest(pageCount, wrapEditTextInViewGroup).apply {
-            repeat(pageCount) {
+            (0 until pageCount).forEach { targetPage ->
                 // given
-                val currentItem = viewPager.currentItem
-                assertBasicState(currentItem, null)
-                val targetPage = (currentItem + 1) % pageCount
-                val editText = editTextForPage(
-                    viewPager.linearLayoutManager.findViewByPosition
-                        (targetPage)!!
-                )
+                viewPager.setCurrentItemSync(targetPage, false, 2, SECONDS)
+                assertBasicState(targetPage, null)
+
+                val otherPage = (targetPage + 1) % pageCount
+                val editText =
+                    editTextForPage(viewPager.linearLayoutManager.findViewByPosition(otherPage)!!)
 
                 // when
                 closeSoftKeyboard() // setCurrentItem ignored otherwise; TODO: check if on purpose
-                val latch = viewPager.addWaitForScrolledLatch(targetPage)
+                val latch = viewPager.addWaitForFirstScrollEventLatch()
                 runOnUiThreadSync { editText.requestFocus() }
-                latch.await(2, SECONDS)
+                assertFalse(latch.await(1, SECONDS)) // TODO: replace with Robolectric
 
                 // then
                 assertBasicState(targetPage, null)
diff --git a/viewpager2/src/main/java/androidx/viewpager2/widget/ViewPager2.java b/viewpager2/src/main/java/androidx/viewpager2/widget/ViewPager2.java
index 772699c..ed39c81 100644
--- a/viewpager2/src/main/java/androidx/viewpager2/widget/ViewPager2.java
+++ b/viewpager2/src/main/java/androidx/viewpager2/widget/ViewPager2.java
@@ -1013,14 +1013,7 @@
         public boolean requestChildRectangleOnScreen(@NonNull RecyclerView parent,
                 @NonNull View child, @NonNull Rect rect, boolean immediate,
                 boolean focusedChildVisible) {
-            int position = getPosition(child);
-            if (getCurrentItem() == position) {
-                return false;
-            }
-
-            boolean smoothScroll = !immediate;
-            setCurrentItem(position, smoothScroll);
-            return smoothScroll;
+            return false; // users should use setCurrentItem instead
         }
     }
 
diff --git a/webkit/integration-tests/testapp/src/main/AndroidManifest.xml b/webkit/integration-tests/testapp/src/main/AndroidManifest.xml
index c827c61..abb80ac 100644
--- a/webkit/integration-tests/testapp/src/main/AndroidManifest.xml
+++ b/webkit/integration-tests/testapp/src/main/AndroidManifest.xml
@@ -20,6 +20,7 @@
     tools:ignore="GoogleAppIndexingWarning">
 
     <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
 
     <application
         android:allowBackup="false"
@@ -92,5 +93,8 @@
         <activity
             android:name=".MultiProcessEnabledActivity"
             android:exported="true" />
+        <activity
+            android:name=".TracingControllerActivity"
+            android:exported="true" />
     </application>
 </manifest>
diff --git a/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/MainActivity.java b/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/MainActivity.java
index e0dd98f..ed37250 100644
--- a/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/MainActivity.java
+++ b/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/MainActivity.java
@@ -57,6 +57,9 @@
                 new MenuListView.MenuItem(
                         getResources().getString(R.string.multi_process_enabled_activity_title),
                         new Intent(activityContext, MultiProcessEnabledActivity.class)),
+                new MenuListView.MenuItem(
+                        getResources().getString(R.string.tracing_controller_activity_title),
+                        new Intent(activityContext, TracingControllerActivity.class)),
         };
         listView.setItems(menuItems);
     }
diff --git a/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/TracingControllerActivity.java b/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/TracingControllerActivity.java
new file mode 100644
index 0000000..209b269
--- /dev/null
+++ b/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/TracingControllerActivity.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2019 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 com.example.androidx.webkit;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.inputmethod.EditorInfo;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.webkit.TracingConfig;
+import androidx.webkit.TracingController;
+import androidx.webkit.WebViewFeature;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+/**
+ * An {@link Activity} to exercise Tracing Controller functionality.
+ */
+public class TracingControllerActivity extends AppCompatActivity {
+    TracingController mTracingController;
+    private WebView mWebView;
+    private TextView mInfo;
+    private EditText mNavigationBar;
+    private Button mButton;
+    private String mLogPath;
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_tracing_controller);
+        setTitle(R.string.tracing_controller_activity_title);
+
+        mNavigationBar = findViewById(R.id.tracing_controller_edittext);
+        mNavigationBar.setOnEditorActionListener((TextView v, int actionId, KeyEvent event) -> {
+            if (actionId == EditorInfo.IME_ACTION_NEXT) {
+                String url = mNavigationBar.getText().toString();
+                if (!url.isEmpty()) {
+                    if (!url.startsWith("http")) url = "http://" + url;
+                    mWebView.loadUrl(url);
+                    mNavigationBar.setText("");
+                }
+                return true;
+            }
+            return false;
+        });
+
+        mInfo = findViewById(R.id.tracing_controller_textview);
+        mInfo.setVisibility(View.GONE);
+
+        mButton = findViewById(R.id.tracing_controller_button);
+        mButton.setOnClickListener(v -> {
+            if (mTracingController.isTracing()) {
+                try {
+                    mButton.setEnabled(false);
+                    mLogPath = getExternalFilesDir(null) + File.separator + "tc.json";
+                    FileOutputStream os = new FileOutputStream(new File(mLogPath)) {
+                        @Override
+                        public void close() throws IOException {
+                            super.close();
+                            runOnUiThread(() -> {
+                                mInfo.setVisibility(View.VISIBLE);
+                                mButton.setVisibility(View.GONE);
+                                mInfo.setText(
+                                        getString(R.string.tracing_controller_log_path, mLogPath));
+                                try {
+                                    verifyJSON();
+                                } catch (IOException | JSONException e) {
+                                    mInfo.setText(R.string.tracing_controller_invalid_log);
+                                }
+                            });
+                        }
+                    };
+                    ExecutorService executor = Executors.newSingleThreadExecutor();
+                    mTracingController.stop(os, executor);
+                } catch (FileNotFoundException e) {
+                    e.printStackTrace();
+                }
+            } else {
+                TracingConfig config = new TracingConfig.Builder()
+                        .addCategories(TracingConfig.CATEGORIES_ANDROID_WEBVIEW)
+                        .build();
+                mTracingController.start(config);
+                mButton.setText(R.string.tracing_controller_stop_tracing);
+            }
+        });
+
+        mWebView = findViewById(R.id.tracing_controller_webview);
+        mWebView.setWebViewClient(new WebViewClient());
+
+        WebkitHelpers.appendWebViewVersionToTitle(this);
+        if (!WebViewFeature.isFeatureSupported(WebViewFeature.TRACING_CONTROLLER_BASIC_USAGE)) {
+            mNavigationBar.setVisibility(View.GONE);
+            mWebView.setVisibility(View.GONE);
+            mButton.setVisibility(View.GONE);
+            mInfo.setVisibility(View.GONE);
+            WebkitHelpers.showMessageInActivity(this, R.string.webkit_api_not_available);
+            return;
+        }
+
+        mTracingController = TracingController.getInstance();
+    }
+
+    private void verifyJSON() throws IOException, JSONException {
+        StringBuilder builder = new StringBuilder();
+        BufferedReader br = new BufferedReader(new FileReader(mLogPath));
+        String sCurrentLine;
+        while ((sCurrentLine = br.readLine()) != null) {
+            builder.append(sCurrentLine).append("\n");
+        }
+
+        // Throw exception if JSON is incorrect
+        new JSONObject(builder.toString());
+    }
+}
diff --git a/webkit/integration-tests/testapp/src/main/res/layout/activity_tracing_controller.xml b/webkit/integration-tests/testapp/src/main/res/layout/activity_tracing_controller.xml
new file mode 100644
index 0000000..fcffdd6
--- /dev/null
+++ b/webkit/integration-tests/testapp/src/main/res/layout/activity_tracing_controller.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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.
+-->
+
+<RelativeLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context=".TracingControllerActivity">
+
+    <TextView
+        android:id="@+id/tracing_controller_textview"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:textSize="22sp"
+        android:layout_marginTop="12dp"
+        android:layout_marginLeft="8dp"
+        android:layout_marginRight="8dp"/>
+
+    <EditText
+        android:id="@+id/tracing_controller_edittext"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginLeft="6dp"
+        android:layout_marginRight="6dp"
+        android:maxLines="1"
+        android:inputType="textUri"
+        android:hint="Type an URL here"
+        android:layout_below="@id/tracing_controller_textview"/>
+
+    <Button
+        android:id="@+id/tracing_controller_button"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/tracing_controller_start_tracing"
+        android:layout_below="@id/tracing_controller_edittext"/>
+
+    <WebView
+        android:id="@+id/tracing_controller_webview"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_below="@id/tracing_controller_button" />
+
+</RelativeLayout>
diff --git a/webkit/integration-tests/testapp/src/main/res/values/strings.xml b/webkit/integration-tests/testapp/src/main/res/values/strings.xml
index 13c5706..8d04851 100644
--- a/webkit/integration-tests/testapp/src/main/res/values/strings.xml
+++ b/webkit/integration-tests/testapp/src/main/res/values/strings.xml
@@ -56,4 +56,10 @@
     <string name="multi_process_enabled">Multi Process is enabled</string>
     <string name="multi_process_disabled">Multi Process is disabled</string>
     <string name="multi_process_query_unavailable">Multi Process query not available</string>
+    <string name="tracing_controller_activity_title">Tracing Controller Demo</string>
+    <string name="tracing_controller_start_tracing">Start tracing</string>
+    <string name="tracing_controller_stop_tracing">Stop tracing</string>
+    <string name="tracing_controller_invalid_log">Log is invalid</string>
+    <string name="tracing_controller_log_path">Wrote log to %1$s. This file can be loaded in
+        chrome://tracing for further analysis.</string>>
 </resources>
diff --git a/work/workmanager/api/api_lint.ignore b/work/workmanager/api/api_lint.ignore
index 8bca693..1381c66 100644
--- a/work/workmanager/api/api_lint.ignore
+++ b/work/workmanager/api/api_lint.ignore
@@ -23,3 +23,7 @@
     Missing nullability on parameter `o` in method `equals`
 MissingNullability: androidx.work.WorkInfo#toString():
     Missing nullability on method `toString` return
+
+
+SetterReturnsThis: androidx.work.WorkRequest.Builder:
+    Methods must return the builder object (return type Builder instead of B): method androidx.work.WorkRequest.Builder.setBackoffCriteria(androidx.work.BackoffPolicy,long,java.util.concurrent.TimeUnit)