Merge changes from topic "ui-material-migration" into androidx-master-dev

* changes:
  Optimizes imports for compose:material:* after the package migration
  Moves ui-material to compose/material/material
diff --git a/buildSrc/build_dependencies.gradle b/buildSrc/build_dependencies.gradle
index f319223..657db32 100644
--- a/buildSrc/build_dependencies.gradle
+++ b/buildSrc/build_dependencies.gradle
@@ -20,7 +20,7 @@
 
 // NOTE: lint versions *must* be kept in sync with agp
 if (isUiProject) {
-    build_versions.kotlin = "1.4.0-rc"
+    build_versions.kotlin = "1.4.0"
     build_versions.kotlin_coroutines = "1.3.6"
     build_versions.agp = '4.2.0-alpha06'
     build_versions.lint = '27.2.0-alpha06'
diff --git a/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt b/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
index 59c34d4..a8664e3 100644
--- a/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
@@ -80,7 +80,7 @@
     val MEDIA2 = Version("1.1.0-alpha02")
     val MEDIAROUTER = Version("1.2.0-alpha02")
     val NAVIGATION = Version("2.4.0-alpha01")
-    val PAGING = Version("3.0.0-alpha05")
+    val PAGING = Version("3.0.0-alpha06")
     val PALETTE = Version("1.1.0-alpha01")
     val PRINT = Version("1.1.0-alpha01")
     val PERCENTLAYOUT = Version("1.1.0-alpha01")
diff --git a/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt b/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt
index f26a85b..4a2d1a6 100644
--- a/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt
@@ -93,6 +93,15 @@
 const val SKIKO_LINUX = "org.jetbrains.skiko:skiko-jvm-runtime-linux:$SKIKO_VERSION"
 const val SKIKO_MACOS = "org.jetbrains.skiko:skiko-jvm-runtime-macos:$SKIKO_VERSION"
 const val SKIKO_WINDOWS = "org.jetbrains.skiko:skiko-jvm-runtime-windows:$SKIKO_VERSION"
+val SKIKO_CURRENT_OS by lazy {
+    val os = System.getProperty("os.name")
+    when {
+        os == "Mac OS X" -> SKIKO_MACOS
+        os.startsWith("Win") -> SKIKO_WINDOWS
+        os.startsWith("Linux") -> SKIKO_LINUX
+        else -> throw Error("Unsupported OS $os")
+    }
+}
 const val TRUTH = "com.google.truth:truth:1.0.1"
 const val XERIAL = "org.xerial:sqlite-jdbc:3.25.2"
 const val XPP3 = "xpp3:xpp3:1.1.4c"
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/Camera2DeviceSurfaceManagerTest.java b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/Camera2DeviceSurfaceManagerTest.java
index 4917fa9..d0441f7 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/Camera2DeviceSurfaceManagerTest.java
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/Camera2DeviceSurfaceManagerTest.java
@@ -30,6 +30,7 @@
 import android.graphics.ImageFormat;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CameraManager;
+import android.hardware.camera2.params.StreamConfigurationMap;
 import android.os.Build;
 import android.util.Size;
 import android.view.WindowManager;
@@ -90,9 +91,7 @@
 @SmallTest
 @RunWith(RobolectricTestRunner.class)
 @DoNotInstrument
-@Config(minSdk = Build.VERSION_CODES.LOLLIPOP,
-        maxSdk = Build.VERSION_CODES.P //TODO (b/149669465) : Some robolectric tests will fail on Q
-)
+@Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
 public final class Camera2DeviceSurfaceManagerTest {
     private static final String LEGACY_CAMERA_ID = "0";
     private static final String LIMITED_CAMERA_ID = "1";
@@ -548,10 +547,21 @@
         ((ShadowCameraManager) Shadow.extract(cameraManager))
                 .addCamera(cameraId, characteristics);
 
-        shadowCharacteristics.set(
-                CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP,
-                StreamConfigurationMapUtil.generateFakeStreamConfigurationMap(
-                        mSupportedFormats, mSupportedSizes));
+        // Current robolectric can support to directly mock a StreamConfigurationMap object if
+        // the testing platform target is equal to or newer than API level 23. For API level 21
+        // or 22 testing platform target, keep the original method to create a
+        // StreamConfigurationMap object via reflection.
+        if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP_MR1) {
+            shadowCharacteristics.set(
+                    CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP,
+                    StreamConfigurationMapUtil.generateFakeStreamConfigurationMap(mSupportedFormats,
+                            mSupportedSizes));
+        } else {
+            StreamConfigurationMap mockMap = mock(StreamConfigurationMap.class);
+            when(mockMap.getOutputSizes(anyInt())).thenReturn(mSupportedSizes);
+            shadowCharacteristics.set(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP,
+                    mockMap);
+        }
 
         @CameraSelector.LensFacing int lensFacingEnum = CameraUtil.getLensFacingEnumFromInt(
                 lensFacing);
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedSizeConstraintsTest.java b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedSizeConstraintsTest.java
index b12db61..e444a45b 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedSizeConstraintsTest.java
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedSizeConstraintsTest.java
@@ -26,6 +26,7 @@
 import android.graphics.ImageFormat;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CameraManager;
+import android.hardware.camera2.params.StreamConfigurationMap;
 import android.os.Build;
 import android.util.Size;
 
@@ -72,9 +73,7 @@
 @SmallTest
 @RunWith(RobolectricTestRunner.class)
 @DoNotInstrument
-@Config(minSdk = Build.VERSION_CODES.LOLLIPOP,
-        maxSdk = Build.VERSION_CODES.P //TODO (b/149669465) : Some robolectric tests will fail on Q
-)
+@Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
 public class SupportedSizeConstraintsTest {
     private static final String BACK_CAMERA_ID = "0";
     private static final int DEFAULT_SENSOR_ORIENTATION = 90;
@@ -223,10 +222,21 @@
 
         int[] supportedFormats = mSupportedFormats;
 
-        shadowCharacteristics.set(
-                CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP,
-                StreamConfigurationMapUtil.generateFakeStreamConfigurationMap(supportedFormats,
-                        supportedSizes));
+        // Current robolectric can support to directly mock a StreamConfigurationMap object if
+        // the testing platform target is equal to or newer than API level 23. For API level 21
+        // or 22 testing platform target, keep the original method to create a
+        // StreamConfigurationMap object via reflection.
+        if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP_MR1) {
+            shadowCharacteristics.set(
+                    CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP,
+                    StreamConfigurationMapUtil.generateFakeStreamConfigurationMap(supportedFormats,
+                            supportedSizes));
+        } else {
+            StreamConfigurationMap mockMap = mock(StreamConfigurationMap.class);
+            when(mockMap.getOutputSizes(anyInt())).thenReturn(supportedSizes);
+            shadowCharacteristics.set(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP,
+                    mockMap);
+        }
 
         @CameraSelector.LensFacing int lensFacingEnum = CameraUtil.getLensFacingEnumFromInt(
                 CameraCharacteristics.LENS_FACING_BACK);
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedSurfaceCombinationTest.java b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedSurfaceCombinationTest.java
index 2064fb8..08fb3e5 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedSurfaceCombinationTest.java
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedSurfaceCombinationTest.java
@@ -29,6 +29,7 @@
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CameraManager;
 import android.hardware.camera2.CameraMetadata;
+import android.hardware.camera2.params.StreamConfigurationMap;
 import android.os.Build;
 import android.util.Pair;
 import android.util.Rational;
@@ -93,9 +94,7 @@
 @SmallTest
 @RunWith(RobolectricTestRunner.class)
 @DoNotInstrument
-@Config(minSdk = Build.VERSION_CODES.LOLLIPOP,
-        maxSdk = Build.VERSION_CODES.P //TODO (b/149669465) : Some robolectric tests will fail on Q
-)
+@Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
 public final class SupportedSurfaceCombinationTest {
     private static final String CAMERA_ID = "0";
     private static final int DEFAULT_SENSOR_ORIENTATION = 90;
@@ -1899,10 +1898,21 @@
         int[] supportedFormats = isRawSupported(capabilities)
                 ? mSupportedFormatsWithRaw : mSupportedFormats;
 
-        shadowCharacteristics.set(
-                CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP,
-                StreamConfigurationMapUtil.generateFakeStreamConfigurationMap(supportedFormats,
-                        supportedSizes));
+        // Current robolectric can support to directly mock a StreamConfigurationMap object if
+        // the testing platform target is equal to or newer than API level 23. For API level 21
+        // or 22 testing platform target, keep the original method to create a
+        // StreamConfigurationMap object via reflection.
+        if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP_MR1) {
+            shadowCharacteristics.set(
+                    CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP,
+                    StreamConfigurationMapUtil.generateFakeStreamConfigurationMap(supportedFormats,
+                            supportedSizes));
+        } else {
+            StreamConfigurationMap mockMap = mock(StreamConfigurationMap.class);
+            when(mockMap.getOutputSizes(anyInt())).thenReturn(supportedSizes);
+            shadowCharacteristics.set(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP,
+                    mockMap);
+        }
 
         @CameraSelector.LensFacing int lensFacingEnum = CameraUtil.getLensFacingEnumFromInt(
                 CameraCharacteristics.LENS_FACING_BACK);
diff --git a/camera/camera-core/src/androidTest/java/androidx/camera/core/internal/CameraUseCaseAdapterTest.java b/camera/camera-core/src/androidTest/java/androidx/camera/core/internal/CameraUseCaseAdapterTest.java
index 187e794..5fb4ba1 100644
--- a/camera/camera-core/src/androidTest/java/androidx/camera/core/internal/CameraUseCaseAdapterTest.java
+++ b/camera/camera-core/src/androidTest/java/androidx/camera/core/internal/CameraUseCaseAdapterTest.java
@@ -19,10 +19,15 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 
+import android.util.Rational;
+import android.view.Surface;
+
 import androidx.camera.core.UseCase;
+import androidx.camera.core.ViewPort;
 import androidx.camera.core.impl.CameraInternal;
 import androidx.camera.testing.fakes.FakeCamera;
 import androidx.camera.testing.fakes.FakeCameraDeviceSurfaceManager;
@@ -174,4 +179,35 @@
 
         verify(callback).onUnbind();
     }
+
+    @Test
+    public void addExistingUseCase_viewPortUpdated()
+            throws CameraUseCaseAdapter.CameraException {
+        Rational aspectRatio1 = new Rational(1, 1);
+        Rational aspectRatio2 = new Rational(2, 1);
+
+        // Arrange: set up adapter with aspect ratio 1.
+        CameraUseCaseAdapter cameraUseCaseAdapter = new CameraUseCaseAdapter(mFakeCamera,
+                mFakeCameraSet,
+                mFakeCameraDeviceSurfaceManager);
+        cameraUseCaseAdapter.setViewPort(
+                new ViewPort.Builder(aspectRatio1, Surface.ROTATION_0).build());
+        FakeUseCase fakeUseCase = spy(new FakeUseCase());
+        cameraUseCaseAdapter.addUseCases(Collections.singleton(fakeUseCase));
+        // Use case gets aspect ratio 1
+        assertThat(fakeUseCase.getViewPortCropRect()).isNotNull();
+        assertThat(new Rational(fakeUseCase.getViewPortCropRect().width(),
+                fakeUseCase.getViewPortCropRect().height())).isEqualTo(aspectRatio1);
+
+        // Act: set aspect ratio 2 and attach the same use case.
+        reset(fakeUseCase);
+        cameraUseCaseAdapter.setViewPort(
+                new ViewPort.Builder(aspectRatio2, Surface.ROTATION_0).build());
+        cameraUseCaseAdapter.addUseCases(Collections.singleton(fakeUseCase));
+
+        // Assert: the viewport has aspect ratio 2.
+        assertThat(fakeUseCase.getViewPortCropRect()).isNotNull();
+        assertThat(new Rational(fakeUseCase.getViewPortCropRect().width(),
+                fakeUseCase.getViewPortCropRect().height())).isEqualTo(aspectRatio2);
+    }
 }
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/UseCase.java b/camera/camera-core/src/main/java/androidx/camera/core/UseCase.java
index a2ca7d4..87be783 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/UseCase.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/UseCase.java
@@ -564,8 +564,7 @@
      */
     @RestrictTo(Scope.LIBRARY)
     @Nullable
-    @SuppressWarnings("KotlinPropertyAccess")
-    protected Rect getViewPortCropRect() {
+    public Rect getViewPortCropRect() {
         return mViewPortCropRect;
     }
 
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/internal/CameraUseCaseAdapter.java b/camera/camera-core/src/main/java/androidx/camera/core/internal/CameraUseCaseAdapter.java
index 6814c12..365a3a4 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/internal/CameraUseCaseAdapter.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/internal/CameraUseCaseAdapter.java
@@ -157,7 +157,7 @@
 
             for (UseCase useCase : useCases) {
                 if (mUseCases.contains(useCase)) {
-                    Log.e(TAG, "Attempting to attach already attached UseCase");
+                    Log.d(TAG, "Attempting to attach already attached UseCase");
                 } else {
                     useCaseListAfterUpdate.add(useCase);
                     newUseCases.add(useCase);
@@ -189,7 +189,8 @@
                         mViewPort.getLayoutDirection(),
                         suggestedResolutionsMap);
                 for (UseCase useCase : useCases) {
-                    useCase.setViewPortCropRect(cropRectMap.get(useCase));
+                    useCase.setViewPortCropRect(
+                            Preconditions.checkNotNull(cropRectMap.get(useCase)));
                 }
             }
 
@@ -279,38 +280,42 @@
             @NonNull List<UseCase> currentUseCases) {
         List<SurfaceConfig> existingSurfaces = new ArrayList<>();
         String cameraId = mCameraInternal.getCameraInfoInternal().getCameraId();
+        Map<UseCase, Size> suggestedResolutions = new HashMap<>();
 
-        Map<UseCaseConfig<?>, UseCase> configToUseCaseMap = new HashMap<>();
-
+        // Get resolution for current use cases.
         for (UseCase useCase : currentUseCases) {
             SurfaceConfig surfaceConfig =
                     mCameraDeviceSurfaceManager.transformSurfaceConfig(cameraId,
                             useCase.getImageFormat(),
                             useCase.getAttachedSurfaceResolution());
             existingSurfaces.add(surfaceConfig);
+            suggestedResolutions.put(useCase, useCase.getAttachedSurfaceResolution());
         }
 
-        for (UseCase useCase : newUseCases) {
-            UseCaseConfig.Builder<?, ?, ?> defaultBuilder = useCase.getDefaultBuilder(
-                    mCameraInternal.getCameraInfoInternal());
+        // Calculate resolution for new use cases.
+        if (!newUseCases.isEmpty()) {
+            Map<UseCaseConfig<?>, UseCase> configToUseCaseMap = new HashMap<>();
+            for (UseCase useCase : newUseCases) {
+                UseCaseConfig.Builder<?, ?, ?> defaultBuilder = useCase.getDefaultBuilder(
+                        mCameraInternal.getCameraInfoInternal());
 
-            // Combine with default configuration.
-            UseCaseConfig<?> combinedUseCaseConfig =
-                    useCase.applyDefaults(useCase.getUseCaseConfig(),
-                            defaultBuilder);
-            configToUseCaseMap.put(combinedUseCaseConfig, useCase);
+                // Combine with default configuration.
+                UseCaseConfig<?> combinedUseCaseConfig =
+                        useCase.applyDefaults(useCase.getUseCaseConfig(),
+                                defaultBuilder);
+                configToUseCaseMap.put(combinedUseCaseConfig, useCase);
+            }
+
+            // Get suggested resolutions and update the use case session configuration
+            Map<UseCaseConfig<?>, Size> useCaseConfigSizeMap = mCameraDeviceSurfaceManager
+                    .getSuggestedResolutions(cameraId, existingSurfaces,
+                            new ArrayList<>(configToUseCaseMap.keySet()));
+
+            for (Map.Entry<UseCaseConfig<?>, UseCase> entry : configToUseCaseMap.entrySet()) {
+                suggestedResolutions.put(entry.getValue(),
+                        useCaseConfigSizeMap.get(entry.getKey()));
+            }
         }
-
-        // Get suggested resolutions and update the use case session configuration
-        Map<UseCaseConfig<?>, Size> useCaseConfigSizeMap = mCameraDeviceSurfaceManager
-                .getSuggestedResolutions(cameraId, existingSurfaces,
-                        new ArrayList<>(configToUseCaseMap.keySet()));
-
-        Map<UseCase, Size> suggestedResolutions = new HashMap<>();
-        for (Map.Entry<UseCaseConfig<?>, UseCase> entry : configToUseCaseMap.entrySet()) {
-            suggestedResolutions.put(entry.getValue(), useCaseConfigSizeMap.get(entry.getKey()));
-        }
-
         return suggestedResolutions;
     }
 
@@ -333,6 +338,7 @@
      */
     public static final class CameraId {
         private final List<String> mIds;
+
         CameraId(LinkedHashSet<CameraInternal> cameraInternals) {
             mIds = new ArrayList<>();
             for (CameraInternal cameraInternal : cameraInternals) {
diff --git a/camera/camera-view/src/androidTest/java/androidx/camera/view/PreviewViewTest.java b/camera/camera-view/src/androidTest/java/androidx/camera/view/PreviewViewTest.java
index 36c09c5..3b36b223 100644
--- a/camera/camera-view/src/androidTest/java/androidx/camera/view/PreviewViewTest.java
+++ b/camera/camera-view/src/androidTest/java/androidx/camera/view/PreviewViewTest.java
@@ -31,6 +31,7 @@
 import static org.mockito.Mockito.when;
 
 import android.Manifest;
+import android.app.Instrumentation;
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.SurfaceTexture;
@@ -63,6 +64,7 @@
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.LargeTest;
+import androidx.test.platform.app.InstrumentationRegistry;
 import androidx.test.rule.ActivityTestRule;
 import androidx.test.rule.GrantPermissionRule;
 
@@ -90,6 +92,8 @@
     @Rule
     public final GrantPermissionRule mRuntimePermissionRule = GrantPermissionRule.grant(
             Manifest.permission.CAMERA);
+
+    private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
     @Rule
     public final ActivityTestRule<FakeActivity> mActivityRule = new ActivityTestRule<>(
             FakeActivity.class);
@@ -277,27 +281,171 @@
     }
 
     @Test
-    @UiThreadTest
-    public void canCreateMeteringPointFactory() {
+    public void canCreateValidMeteringPoint() throws Exception {
         final CameraInfo cameraInfo = createCameraInfo(90,
                 CameraInfo.IMPLEMENTATION_TYPE_CAMERA2, CameraSelector.LENS_FACING_BACK);
 
         final PreviewView previewView = new PreviewView(mContext);
-        setContentView(previewView);
 
-        Preview.SurfaceProvider surfaceProvider = previewView.createSurfaceProvider();
-        mSurfaceRequest = createSurfaceRequest(cameraInfo);
-        surfaceProvider.onSurfaceRequested(mSurfaceRequest);
+        mInstrumentation.runOnMainSync(() -> {
+            setContentView(previewView);
+            mSurfaceRequest = createSurfaceRequest(cameraInfo);
+            Preview.SurfaceProvider surfaceProvider = previewView.createSurfaceProvider();
+            surfaceProvider.onSurfaceRequested(mSurfaceRequest);
+        });
+
+        waitForLayoutReady(previewView);
+
+        MeteringPointFactory factory = previewView.getMeteringPointFactory();
+        MeteringPoint point = factory.createPoint(100, 100);
+        assertPointIsValid(point);
+    }
+
+    private void assertPointIsValid(MeteringPoint point) {
+        assertThat(point.getX() >= 0f && point.getX() <= 1.0f).isTrue();
+        assertThat(point.getY() >= 0f && point.getY() <= 1.0f).isTrue();
+    }
+
+    @Test
+    public void meteringPointFactoryAutoAdjusted_whenViewSizeChange() throws Exception {
+        final CameraInfo cameraInfo = createCameraInfo(90,
+                CameraInfo.IMPLEMENTATION_TYPE_CAMERA2, CameraSelector.LENS_FACING_BACK);
+
+        final PreviewView previewView = new PreviewView(mContext);
         MeteringPointFactory factory = previewView.getMeteringPointFactory();
 
-        MeteringPoint point = factory.createPoint(100, 100);
-        assertThat(point.getX() >= 0f || point.getX() <= 1.0f);
-        assertThat(point.getY() >= 0f || point.getY() <= 1.0f);
+        mInstrumentation.runOnMainSync(() -> {
+            setContentView(previewView);
+            mSurfaceRequest = createSurfaceRequest(cameraInfo);
+            Preview.SurfaceProvider surfaceProvider = previewView.createSurfaceProvider();
+            surfaceProvider.onSurfaceRequested(mSurfaceRequest);
+        });
+
+        changeViewSize(previewView, 1000, 1000);
+        MeteringPoint point1 = factory.createPoint(100, 100);
+
+        changeViewSize(previewView, 500, 400);
+
+        MeteringPoint point2 = factory.createPoint(100, 100);
+
+        assertPointIsValid(point1);
+        assertPointIsValid(point2);
+        // These points should be different because the layout is changed.
+        assertPointsAreDifferent(point1, point2);
+    }
+
+    private void changeViewSize(PreviewView previewView, int newWidth, int newHeight)
+            throws InterruptedException {
+        CountDownLatch latchToWaitForLayoutChange = new CountDownLatch(1);
+        mInstrumentation.runOnMainSync(() -> {
+            previewView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
+                @Override
+                public void onLayoutChange(View v, int left, int top, int right, int bottom,
+                        int oldLeft,
+                        int oldTop, int oldRight, int oldBottom) {
+                    if (previewView.getWidth() == newWidth
+                            && previewView.getHeight() == newHeight) {
+                        latchToWaitForLayoutChange.countDown();
+                        previewView.removeOnLayoutChangeListener(this);
+                    }
+                }
+            });
+            previewView.setLayoutParams(new FrameLayout.LayoutParams(newWidth, newHeight));
+        });
+
+        // Wait until the new layout is changed.
+        assertThat(latchToWaitForLayoutChange.await(1, TimeUnit.SECONDS)).isTrue();
+    }
+
+    @Test
+    public void meteringPointFactoryAutoAdjusted_whenScaleTypeChanged() throws Exception {
+        final CameraInfo cameraInfo = createCameraInfo(90,
+                CameraInfo.IMPLEMENTATION_TYPE_CAMERA2, CameraSelector.LENS_FACING_BACK);
+
+        final PreviewView previewView = new PreviewView(mContext);
+        MeteringPointFactory factory = previewView.getMeteringPointFactory();
+
+        mInstrumentation.runOnMainSync(() -> {
+            setContentView(previewView);
+            mSurfaceRequest = createSurfaceRequest(cameraInfo);
+            Preview.SurfaceProvider surfaceProvider = previewView.createSurfaceProvider();
+            surfaceProvider.onSurfaceRequested(mSurfaceRequest);
+        });
+        // Surface resolution is 640x480 , set a different size for PreviewView.
+        changeViewSize(previewView, 800, 700);
+
+        previewView.setScaleType(PreviewView.ScaleType.FILL_CENTER);
+        MeteringPoint point1 = factory.createPoint(100, 100);
+
+        previewView.setScaleType(PreviewView.ScaleType.FIT_START);
+        MeteringPoint point2 = factory.createPoint(100, 100);
+
+        assertPointIsValid(point1);
+        assertPointIsValid(point2);
+        // These points should be different
+        assertPointsAreDifferent(point1, point2);
+    }
+
+    @Test
+    public void meteringPointFactoryAutoAdjusted_whenSurfaceRequestChanged() throws Exception {
+        final CameraInfo cameraInfo1 = createCameraInfo(90,
+                CameraInfo.IMPLEMENTATION_TYPE_CAMERA2, CameraSelector.LENS_FACING_BACK);
+        final CameraInfo cameraInfo2 = createCameraInfo(270,
+                CameraInfo.IMPLEMENTATION_TYPE_CAMERA2, CameraSelector.LENS_FACING_FRONT);
+
+        final PreviewView previewView = new PreviewView(mContext);
+        MeteringPointFactory factory = previewView.getMeteringPointFactory();
+
+        mInstrumentation.runOnMainSync(() -> {
+            setContentView(previewView);
+            mSurfaceRequest = createSurfaceRequest(cameraInfo1);
+            Preview.SurfaceProvider surfaceProvider = previewView.createSurfaceProvider();
+            surfaceProvider.onSurfaceRequested(mSurfaceRequest);
+        });
+
+        changeViewSize(previewView, 1000, 1000);
+
+        // get a MeteringPoint from a non-center point.
+        MeteringPoint point1 = factory.createPoint(100, 120);
+
+        mInstrumentation.runOnMainSync(() -> {
+            setContentView(previewView);
+            mSurfaceRequest = createSurfaceRequest(cameraInfo2);
+            Preview.SurfaceProvider surfaceProvider = previewView.createSurfaceProvider();
+            surfaceProvider.onSurfaceRequested(mSurfaceRequest);
+        });
+
+        MeteringPoint point2 = factory.createPoint(100, 120);
+
+        assertPointIsValid(point1);
+        assertPointIsValid(point2);
+        // These points should be different
+        assertPointsAreDifferent(point1, point2);
+    }
+
+    private void assertPointsAreDifferent(MeteringPoint point1, MeteringPoint point2) {
+        assertThat(point1.getX() != point2.getX() || point1.getY() != point2.getY()).isTrue();
+    }
+
+    private void waitForLayoutReady(PreviewView previewView) throws InterruptedException {
+        CountDownLatch countDownLatch = new CountDownLatch(1);
+        previewView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
+            @Override
+            public void onLayoutChange(View v, int left, int top, int right, int bottom,
+                    int oldLeft,
+                    int oldTop, int oldRight, int oldBottom) {
+                if (v.getWidth() > 0 && v.getHeight() > 0) {
+                    countDownLatch.countDown();
+                    previewView.removeOnLayoutChangeListener(this);
+                }
+            }
+        });
+        assertThat(countDownLatch.await(1, TimeUnit.SECONDS)).isTrue();
     }
 
     @Test
     @UiThreadTest
-    public void createMeteringPointFactory_previewViewWidthOrHeightIs0() {
+    public void meteringPointInvalid_whenPreviewViewWidthOrHeightIs0() {
         final CameraInfo cameraInfo = createCameraInfo(90,
                 CameraInfo.IMPLEMENTATION_TYPE_CAMERA2, CameraSelector.LENS_FACING_BACK);
 
@@ -311,13 +459,17 @@
         //Width and height is 0,  but surface is requested,
         //verifying the factory only creates invalid points.
         MeteringPoint point = factory.createPoint(100, 100);
-        assertThat(point.getX() < 0f || point.getX() > 1.0f);
-        assertThat(point.getY() < 0f || point.getY() > 1.0f);
+        assertPointIsInvalid(point);
+    }
+
+    private void assertPointIsInvalid(MeteringPoint point) {
+        assertThat(point.getX() < 0f || point.getX() > 1.0f).isTrue();
+        assertThat(point.getY() < 0f || point.getY() > 1.0f).isTrue();
     }
 
     @Test
     @UiThreadTest
-    public void createMeteringPointFactory_beforeCreatingSurfaceProvider() {
+    public void meteringPointInvalid_beforeCreatingSurfaceProvider() {
         final PreviewView previewView = new PreviewView(mContext);
         // make PreviewView.getWidth() getHeight not 0.
         setContentView(previewView);
@@ -325,8 +477,7 @@
 
         //verifying the factory only creates invalid points.
         MeteringPoint point = factory.createPoint(100, 100);
-        assertThat(point.getX() < 0f || point.getX() > 1.0f);
-        assertThat(point.getY() < 0f || point.getY() > 1.0f);
+        assertPointIsInvalid(point);
     }
 
     @Test
diff --git a/ui/ui-animation-core/OWNERS b/compose/animation/animation-core/OWNERS
similarity index 100%
rename from ui/ui-animation-core/OWNERS
rename to compose/animation/animation-core/OWNERS
diff --git a/ui/ui-animation-core/api/0.1.0-dev01.txt b/compose/animation/animation-core/api/0.1.0-dev01.txt
similarity index 100%
rename from ui/ui-animation-core/api/0.1.0-dev01.txt
rename to compose/animation/animation-core/api/0.1.0-dev01.txt
diff --git a/ui/ui-animation-core/api/0.1.0-dev02.txt b/compose/animation/animation-core/api/0.1.0-dev02.txt
similarity index 100%
rename from ui/ui-animation-core/api/0.1.0-dev02.txt
rename to compose/animation/animation-core/api/0.1.0-dev02.txt
diff --git a/ui/ui-animation-core/api/0.1.0-dev03.txt b/compose/animation/animation-core/api/0.1.0-dev03.txt
similarity index 100%
rename from ui/ui-animation-core/api/0.1.0-dev03.txt
rename to compose/animation/animation-core/api/0.1.0-dev03.txt
diff --git a/ui/ui-animation-core/api/0.1.0-dev04.txt b/compose/animation/animation-core/api/0.1.0-dev04.txt
similarity index 100%
rename from ui/ui-animation-core/api/0.1.0-dev04.txt
rename to compose/animation/animation-core/api/0.1.0-dev04.txt
diff --git a/ui/ui-animation-core/api/0.1.0-dev05.txt b/compose/animation/animation-core/api/0.1.0-dev05.txt
similarity index 100%
rename from ui/ui-animation-core/api/0.1.0-dev05.txt
rename to compose/animation/animation-core/api/0.1.0-dev05.txt
diff --git a/ui/ui-animation-core/api/0.1.0-dev06.txt b/compose/animation/animation-core/api/0.1.0-dev06.txt
similarity index 100%
rename from ui/ui-animation-core/api/0.1.0-dev06.txt
rename to compose/animation/animation-core/api/0.1.0-dev06.txt
diff --git a/ui/ui-animation-core/api/0.1.0-dev07.txt b/compose/animation/animation-core/api/0.1.0-dev07.txt
similarity index 100%
rename from ui/ui-animation-core/api/0.1.0-dev07.txt
rename to compose/animation/animation-core/api/0.1.0-dev07.txt
diff --git a/ui/ui-animation-core/api/0.1.0-dev08.txt b/compose/animation/animation-core/api/0.1.0-dev08.txt
similarity index 100%
rename from ui/ui-animation-core/api/0.1.0-dev08.txt
rename to compose/animation/animation-core/api/0.1.0-dev08.txt
diff --git a/ui/ui-animation-core/api/0.1.0-dev09.txt b/compose/animation/animation-core/api/0.1.0-dev09.txt
similarity index 100%
rename from ui/ui-animation-core/api/0.1.0-dev09.txt
rename to compose/animation/animation-core/api/0.1.0-dev09.txt
diff --git a/ui/ui-animation-core/api/0.1.0-dev10.txt b/compose/animation/animation-core/api/0.1.0-dev10.txt
similarity index 100%
rename from ui/ui-animation-core/api/0.1.0-dev10.txt
rename to compose/animation/animation-core/api/0.1.0-dev10.txt
diff --git a/ui/ui-animation-core/api/0.1.0-dev11.txt b/compose/animation/animation-core/api/0.1.0-dev11.txt
similarity index 100%
rename from ui/ui-animation-core/api/0.1.0-dev11.txt
rename to compose/animation/animation-core/api/0.1.0-dev11.txt
diff --git a/ui/ui-animation-core/api/0.1.0-dev12.txt b/compose/animation/animation-core/api/0.1.0-dev12.txt
similarity index 100%
rename from ui/ui-animation-core/api/0.1.0-dev12.txt
rename to compose/animation/animation-core/api/0.1.0-dev12.txt
diff --git a/ui/ui-animation-core/api/0.1.0-dev14.txt b/compose/animation/animation-core/api/0.1.0-dev14.txt
similarity index 100%
rename from ui/ui-animation-core/api/0.1.0-dev14.txt
rename to compose/animation/animation-core/api/0.1.0-dev14.txt
diff --git a/ui/ui-animation-core/api/0.1.0-dev15.txt b/compose/animation/animation-core/api/0.1.0-dev15.txt
similarity index 100%
rename from ui/ui-animation-core/api/0.1.0-dev15.txt
rename to compose/animation/animation-core/api/0.1.0-dev15.txt
diff --git a/ui/ui-animation-core/api/0.1.0-dev16.txt b/compose/animation/animation-core/api/0.1.0-dev16.txt
similarity index 100%
rename from ui/ui-animation-core/api/0.1.0-dev16.txt
rename to compose/animation/animation-core/api/0.1.0-dev16.txt
diff --git a/ui/ui-animation-core/api/api_lint.ignore b/compose/animation/animation-core/api/api_lint.ignore
similarity index 100%
rename from ui/ui-animation-core/api/api_lint.ignore
rename to compose/animation/animation-core/api/api_lint.ignore
diff --git a/ui/ui-animation-core/api/current.txt b/compose/animation/animation-core/api/current.txt
similarity index 100%
rename from ui/ui-animation-core/api/current.txt
rename to compose/animation/animation-core/api/current.txt
diff --git a/ui/ui-animation-core/api/public_plus_experimental_0.1.0-dev01.txt b/compose/animation/animation-core/api/public_plus_experimental_0.1.0-dev01.txt
similarity index 100%
rename from ui/ui-animation-core/api/public_plus_experimental_0.1.0-dev01.txt
rename to compose/animation/animation-core/api/public_plus_experimental_0.1.0-dev01.txt
diff --git a/ui/ui-animation-core/api/public_plus_experimental_0.1.0-dev02.txt b/compose/animation/animation-core/api/public_plus_experimental_0.1.0-dev02.txt
similarity index 100%
rename from ui/ui-animation-core/api/public_plus_experimental_0.1.0-dev02.txt
rename to compose/animation/animation-core/api/public_plus_experimental_0.1.0-dev02.txt
diff --git a/ui/ui-animation-core/api/public_plus_experimental_0.1.0-dev03.txt b/compose/animation/animation-core/api/public_plus_experimental_0.1.0-dev03.txt
similarity index 100%
rename from ui/ui-animation-core/api/public_plus_experimental_0.1.0-dev03.txt
rename to compose/animation/animation-core/api/public_plus_experimental_0.1.0-dev03.txt
diff --git a/ui/ui-animation-core/api/public_plus_experimental_0.1.0-dev04.txt b/compose/animation/animation-core/api/public_plus_experimental_0.1.0-dev04.txt
similarity index 100%
rename from ui/ui-animation-core/api/public_plus_experimental_0.1.0-dev04.txt
rename to compose/animation/animation-core/api/public_plus_experimental_0.1.0-dev04.txt
diff --git a/ui/ui-animation-core/api/public_plus_experimental_0.1.0-dev05.txt b/compose/animation/animation-core/api/public_plus_experimental_0.1.0-dev05.txt
similarity index 100%
rename from ui/ui-animation-core/api/public_plus_experimental_0.1.0-dev05.txt
rename to compose/animation/animation-core/api/public_plus_experimental_0.1.0-dev05.txt
diff --git a/ui/ui-animation-core/api/public_plus_experimental_0.1.0-dev06.txt b/compose/animation/animation-core/api/public_plus_experimental_0.1.0-dev06.txt
similarity index 100%
rename from ui/ui-animation-core/api/public_plus_experimental_0.1.0-dev06.txt
rename to compose/animation/animation-core/api/public_plus_experimental_0.1.0-dev06.txt
diff --git a/ui/ui-animation-core/api/public_plus_experimental_0.1.0-dev07.txt b/compose/animation/animation-core/api/public_plus_experimental_0.1.0-dev07.txt
similarity index 100%
rename from ui/ui-animation-core/api/public_plus_experimental_0.1.0-dev07.txt
rename to compose/animation/animation-core/api/public_plus_experimental_0.1.0-dev07.txt
diff --git a/ui/ui-animation-core/api/public_plus_experimental_0.1.0-dev08.txt b/compose/animation/animation-core/api/public_plus_experimental_0.1.0-dev08.txt
similarity index 100%
rename from ui/ui-animation-core/api/public_plus_experimental_0.1.0-dev08.txt
rename to compose/animation/animation-core/api/public_plus_experimental_0.1.0-dev08.txt
diff --git a/ui/ui-animation-core/api/public_plus_experimental_0.1.0-dev09.txt b/compose/animation/animation-core/api/public_plus_experimental_0.1.0-dev09.txt
similarity index 100%
rename from ui/ui-animation-core/api/public_plus_experimental_0.1.0-dev09.txt
rename to compose/animation/animation-core/api/public_plus_experimental_0.1.0-dev09.txt
diff --git a/ui/ui-animation-core/api/public_plus_experimental_0.1.0-dev10.txt b/compose/animation/animation-core/api/public_plus_experimental_0.1.0-dev10.txt
similarity index 100%
rename from ui/ui-animation-core/api/public_plus_experimental_0.1.0-dev10.txt
rename to compose/animation/animation-core/api/public_plus_experimental_0.1.0-dev10.txt
diff --git a/ui/ui-animation-core/api/public_plus_experimental_0.1.0-dev11.txt b/compose/animation/animation-core/api/public_plus_experimental_0.1.0-dev11.txt
similarity index 100%
rename from ui/ui-animation-core/api/public_plus_experimental_0.1.0-dev11.txt
rename to compose/animation/animation-core/api/public_plus_experimental_0.1.0-dev11.txt
diff --git a/ui/ui-animation-core/api/public_plus_experimental_0.1.0-dev12.txt b/compose/animation/animation-core/api/public_plus_experimental_0.1.0-dev12.txt
similarity index 100%
rename from ui/ui-animation-core/api/public_plus_experimental_0.1.0-dev12.txt
rename to compose/animation/animation-core/api/public_plus_experimental_0.1.0-dev12.txt
diff --git a/ui/ui-animation-core/api/public_plus_experimental_0.1.0-dev14.txt b/compose/animation/animation-core/api/public_plus_experimental_0.1.0-dev14.txt
similarity index 100%
rename from ui/ui-animation-core/api/public_plus_experimental_0.1.0-dev14.txt
rename to compose/animation/animation-core/api/public_plus_experimental_0.1.0-dev14.txt
diff --git a/ui/ui-animation-core/api/public_plus_experimental_0.1.0-dev15.txt b/compose/animation/animation-core/api/public_plus_experimental_0.1.0-dev15.txt
similarity index 100%
rename from ui/ui-animation-core/api/public_plus_experimental_0.1.0-dev15.txt
rename to compose/animation/animation-core/api/public_plus_experimental_0.1.0-dev15.txt
diff --git a/ui/ui-animation-core/api/public_plus_experimental_0.1.0-dev16.txt b/compose/animation/animation-core/api/public_plus_experimental_0.1.0-dev16.txt
similarity index 100%
rename from ui/ui-animation-core/api/public_plus_experimental_0.1.0-dev16.txt
rename to compose/animation/animation-core/api/public_plus_experimental_0.1.0-dev16.txt
diff --git a/ui/ui-animation-core/api/public_plus_experimental_current.txt b/compose/animation/animation-core/api/public_plus_experimental_current.txt
similarity index 100%
rename from ui/ui-animation-core/api/public_plus_experimental_current.txt
rename to compose/animation/animation-core/api/public_plus_experimental_current.txt
diff --git a/ui/ui-animation-core/api/res-0.1.0-dev01.txt b/compose/animation/animation-core/api/res-0.1.0-dev01.txt
similarity index 100%
rename from ui/ui-animation-core/api/res-0.1.0-dev01.txt
rename to compose/animation/animation-core/api/res-0.1.0-dev01.txt
diff --git a/ui/ui-animation-core/api/res-0.1.0-dev02.txt b/compose/animation/animation-core/api/res-0.1.0-dev02.txt
similarity index 100%
rename from ui/ui-animation-core/api/res-0.1.0-dev02.txt
rename to compose/animation/animation-core/api/res-0.1.0-dev02.txt
diff --git a/ui/ui-animation-core/api/res-0.1.0-dev03.txt b/compose/animation/animation-core/api/res-0.1.0-dev03.txt
similarity index 100%
rename from ui/ui-animation-core/api/res-0.1.0-dev03.txt
rename to compose/animation/animation-core/api/res-0.1.0-dev03.txt
diff --git a/ui/ui-animation-core/api/res-0.1.0-dev04.txt b/compose/animation/animation-core/api/res-0.1.0-dev04.txt
similarity index 100%
rename from ui/ui-animation-core/api/res-0.1.0-dev04.txt
rename to compose/animation/animation-core/api/res-0.1.0-dev04.txt
diff --git a/ui/ui-animation-core/api/res-0.1.0-dev05.txt b/compose/animation/animation-core/api/res-0.1.0-dev05.txt
similarity index 100%
rename from ui/ui-animation-core/api/res-0.1.0-dev05.txt
rename to compose/animation/animation-core/api/res-0.1.0-dev05.txt
diff --git a/ui/ui-animation-core/api/res-0.1.0-dev06.txt b/compose/animation/animation-core/api/res-0.1.0-dev06.txt
similarity index 100%
rename from ui/ui-animation-core/api/res-0.1.0-dev06.txt
rename to compose/animation/animation-core/api/res-0.1.0-dev06.txt
diff --git a/ui/ui-animation-core/api/res-0.1.0-dev07.txt b/compose/animation/animation-core/api/res-0.1.0-dev07.txt
similarity index 100%
rename from ui/ui-animation-core/api/res-0.1.0-dev07.txt
rename to compose/animation/animation-core/api/res-0.1.0-dev07.txt
diff --git a/ui/ui-animation-core/api/res-0.1.0-dev08.txt b/compose/animation/animation-core/api/res-0.1.0-dev08.txt
similarity index 100%
rename from ui/ui-animation-core/api/res-0.1.0-dev08.txt
rename to compose/animation/animation-core/api/res-0.1.0-dev08.txt
diff --git a/ui/ui-animation-core/api/res-0.1.0-dev09.txt b/compose/animation/animation-core/api/res-0.1.0-dev09.txt
similarity index 100%
rename from ui/ui-animation-core/api/res-0.1.0-dev09.txt
rename to compose/animation/animation-core/api/res-0.1.0-dev09.txt
diff --git a/ui/ui-animation-core/api/res-0.1.0-dev10.txt b/compose/animation/animation-core/api/res-0.1.0-dev10.txt
similarity index 100%
rename from ui/ui-animation-core/api/res-0.1.0-dev10.txt
rename to compose/animation/animation-core/api/res-0.1.0-dev10.txt
diff --git a/ui/ui-animation-core/api/res-0.1.0-dev11.txt b/compose/animation/animation-core/api/res-0.1.0-dev11.txt
similarity index 100%
rename from ui/ui-animation-core/api/res-0.1.0-dev11.txt
rename to compose/animation/animation-core/api/res-0.1.0-dev11.txt
diff --git a/ui/ui-animation-core/api/res-0.1.0-dev12.txt b/compose/animation/animation-core/api/res-0.1.0-dev12.txt
similarity index 100%
rename from ui/ui-animation-core/api/res-0.1.0-dev12.txt
rename to compose/animation/animation-core/api/res-0.1.0-dev12.txt
diff --git a/ui/ui-animation-core/api/res-0.1.0-dev14.txt b/compose/animation/animation-core/api/res-0.1.0-dev14.txt
similarity index 100%
rename from ui/ui-animation-core/api/res-0.1.0-dev14.txt
rename to compose/animation/animation-core/api/res-0.1.0-dev14.txt
diff --git a/ui/ui-animation-core/api/res-0.1.0-dev15.txt b/compose/animation/animation-core/api/res-0.1.0-dev15.txt
similarity index 100%
rename from ui/ui-animation-core/api/res-0.1.0-dev15.txt
rename to compose/animation/animation-core/api/res-0.1.0-dev15.txt
diff --git a/ui/ui-animation-core/api/res-0.1.0-dev16.txt b/compose/animation/animation-core/api/res-0.1.0-dev16.txt
similarity index 100%
rename from ui/ui-animation-core/api/res-0.1.0-dev16.txt
rename to compose/animation/animation-core/api/res-0.1.0-dev16.txt
diff --git a/ui/ui-animation-core/api/res-current.txt b/compose/animation/animation-core/api/res-current.txt
similarity index 100%
rename from ui/ui-animation-core/api/res-current.txt
rename to compose/animation/animation-core/api/res-current.txt
diff --git a/ui/ui-animation-core/api/restricted_0.1.0-dev01.txt b/compose/animation/animation-core/api/restricted_0.1.0-dev01.txt
similarity index 100%
rename from ui/ui-animation-core/api/restricted_0.1.0-dev01.txt
rename to compose/animation/animation-core/api/restricted_0.1.0-dev01.txt
diff --git a/ui/ui-animation-core/api/restricted_0.1.0-dev02.txt b/compose/animation/animation-core/api/restricted_0.1.0-dev02.txt
similarity index 100%
rename from ui/ui-animation-core/api/restricted_0.1.0-dev02.txt
rename to compose/animation/animation-core/api/restricted_0.1.0-dev02.txt
diff --git a/ui/ui-animation-core/api/restricted_0.1.0-dev03.txt b/compose/animation/animation-core/api/restricted_0.1.0-dev03.txt
similarity index 100%
rename from ui/ui-animation-core/api/restricted_0.1.0-dev03.txt
rename to compose/animation/animation-core/api/restricted_0.1.0-dev03.txt
diff --git a/ui/ui-animation-core/api/restricted_0.1.0-dev04.txt b/compose/animation/animation-core/api/restricted_0.1.0-dev04.txt
similarity index 100%
rename from ui/ui-animation-core/api/restricted_0.1.0-dev04.txt
rename to compose/animation/animation-core/api/restricted_0.1.0-dev04.txt
diff --git a/ui/ui-animation-core/api/restricted_0.1.0-dev05.txt b/compose/animation/animation-core/api/restricted_0.1.0-dev05.txt
similarity index 100%
rename from ui/ui-animation-core/api/restricted_0.1.0-dev05.txt
rename to compose/animation/animation-core/api/restricted_0.1.0-dev05.txt
diff --git a/ui/ui-animation-core/api/restricted_0.1.0-dev06.txt b/compose/animation/animation-core/api/restricted_0.1.0-dev06.txt
similarity index 100%
rename from ui/ui-animation-core/api/restricted_0.1.0-dev06.txt
rename to compose/animation/animation-core/api/restricted_0.1.0-dev06.txt
diff --git a/ui/ui-animation-core/api/restricted_0.1.0-dev07.txt b/compose/animation/animation-core/api/restricted_0.1.0-dev07.txt
similarity index 100%
rename from ui/ui-animation-core/api/restricted_0.1.0-dev07.txt
rename to compose/animation/animation-core/api/restricted_0.1.0-dev07.txt
diff --git a/ui/ui-animation-core/api/restricted_0.1.0-dev08.txt b/compose/animation/animation-core/api/restricted_0.1.0-dev08.txt
similarity index 100%
rename from ui/ui-animation-core/api/restricted_0.1.0-dev08.txt
rename to compose/animation/animation-core/api/restricted_0.1.0-dev08.txt
diff --git a/ui/ui-animation-core/api/restricted_0.1.0-dev09.txt b/compose/animation/animation-core/api/restricted_0.1.0-dev09.txt
similarity index 100%
rename from ui/ui-animation-core/api/restricted_0.1.0-dev09.txt
rename to compose/animation/animation-core/api/restricted_0.1.0-dev09.txt
diff --git a/ui/ui-animation-core/api/restricted_0.1.0-dev10.txt b/compose/animation/animation-core/api/restricted_0.1.0-dev10.txt
similarity index 100%
rename from ui/ui-animation-core/api/restricted_0.1.0-dev10.txt
rename to compose/animation/animation-core/api/restricted_0.1.0-dev10.txt
diff --git a/ui/ui-animation-core/api/restricted_0.1.0-dev11.txt b/compose/animation/animation-core/api/restricted_0.1.0-dev11.txt
similarity index 100%
rename from ui/ui-animation-core/api/restricted_0.1.0-dev11.txt
rename to compose/animation/animation-core/api/restricted_0.1.0-dev11.txt
diff --git a/ui/ui-animation-core/api/restricted_0.1.0-dev12.txt b/compose/animation/animation-core/api/restricted_0.1.0-dev12.txt
similarity index 100%
rename from ui/ui-animation-core/api/restricted_0.1.0-dev12.txt
rename to compose/animation/animation-core/api/restricted_0.1.0-dev12.txt
diff --git a/ui/ui-animation-core/api/restricted_0.1.0-dev14.txt b/compose/animation/animation-core/api/restricted_0.1.0-dev14.txt
similarity index 100%
rename from ui/ui-animation-core/api/restricted_0.1.0-dev14.txt
rename to compose/animation/animation-core/api/restricted_0.1.0-dev14.txt
diff --git a/ui/ui-animation-core/api/restricted_0.1.0-dev15.txt b/compose/animation/animation-core/api/restricted_0.1.0-dev15.txt
similarity index 100%
rename from ui/ui-animation-core/api/restricted_0.1.0-dev15.txt
rename to compose/animation/animation-core/api/restricted_0.1.0-dev15.txt
diff --git a/ui/ui-animation-core/api/restricted_0.1.0-dev16.txt b/compose/animation/animation-core/api/restricted_0.1.0-dev16.txt
similarity index 100%
rename from ui/ui-animation-core/api/restricted_0.1.0-dev16.txt
rename to compose/animation/animation-core/api/restricted_0.1.0-dev16.txt
diff --git a/ui/ui-animation-core/api/restricted_current.txt b/compose/animation/animation-core/api/restricted_current.txt
similarity index 100%
rename from ui/ui-animation-core/api/restricted_current.txt
rename to compose/animation/animation-core/api/restricted_current.txt
diff --git a/ui/ui-animation-core/build.gradle b/compose/animation/animation-core/build.gradle
similarity index 96%
rename from ui/ui-animation-core/build.gradle
rename to compose/animation/animation-core/build.gradle
index 39aadb2..03dedb5 100644
--- a/ui/ui-animation-core/build.gradle
+++ b/compose/animation/animation-core/build.gradle
@@ -48,7 +48,6 @@
             implementation (KOTLIN_STDLIB)
         }
         desktopMain.dependencies {
-            implementation project(":compose:runtime:runtime-dispatch")
             implementation (KOTLIN_STDLIB)
         }
 
diff --git a/ui/ui-animation-core/samples/build.gradle b/compose/animation/animation-core/samples/build.gradle
similarity index 100%
rename from ui/ui-animation-core/samples/build.gradle
rename to compose/animation/animation-core/samples/build.gradle
diff --git a/ui/ui-animation-core/samples/src/main/AndroidManifest.xml b/compose/animation/animation-core/samples/src/main/AndroidManifest.xml
similarity index 100%
rename from ui/ui-animation-core/samples/src/main/AndroidManifest.xml
rename to compose/animation/animation-core/samples/src/main/AndroidManifest.xml
diff --git a/ui/ui-animation-core/samples/src/main/java/androidx/compose/animation/core/samples/KeyframesBuilderSample.kt b/compose/animation/animation-core/samples/src/main/java/androidx/compose/animation/core/samples/KeyframesBuilderSample.kt
similarity index 99%
rename from ui/ui-animation-core/samples/src/main/java/androidx/compose/animation/core/samples/KeyframesBuilderSample.kt
rename to compose/animation/animation-core/samples/src/main/java/androidx/compose/animation/core/samples/KeyframesBuilderSample.kt
index bcb4aea..186026b 100644
--- a/ui/ui-animation-core/samples/src/main/java/androidx/compose/animation/core/samples/KeyframesBuilderSample.kt
+++ b/compose/animation/animation-core/samples/src/main/java/androidx/compose/animation/core/samples/KeyframesBuilderSample.kt
@@ -16,12 +16,12 @@
 
 package androidx.compose.animation.core.samples
 
+import androidx.annotation.Sampled
 import androidx.compose.animation.core.FastOutSlowInEasing
 import androidx.compose.animation.core.KeyframesSpec
 import androidx.compose.animation.core.LinearEasing
 import androidx.compose.animation.core.LinearOutSlowInEasing
 import androidx.compose.animation.core.keyframes
-import androidx.annotation.Sampled
 import androidx.compose.ui.unit.Position
 import androidx.compose.ui.unit.dp
 
diff --git a/ui/ui-animation-core/samples/src/main/java/androidx/compose/animation/core/samples/TransitionDefinitionSamples.kt b/compose/animation/animation-core/samples/src/main/java/androidx/compose/animation/core/samples/TransitionDefinitionSamples.kt
similarity index 99%
rename from ui/ui-animation-core/samples/src/main/java/androidx/compose/animation/core/samples/TransitionDefinitionSamples.kt
rename to compose/animation/animation-core/samples/src/main/java/androidx/compose/animation/core/samples/TransitionDefinitionSamples.kt
index 189b7af..ca07aac 100644
--- a/ui/ui-animation-core/samples/src/main/java/androidx/compose/animation/core/samples/TransitionDefinitionSamples.kt
+++ b/compose/animation/animation-core/samples/src/main/java/androidx/compose/animation/core/samples/TransitionDefinitionSamples.kt
@@ -16,12 +16,12 @@
 
 package androidx.compose.animation.core.samples
 
+import androidx.annotation.Sampled
 import androidx.compose.animation.core.FloatPropKey
 import androidx.compose.animation.core.InterruptionHandling
 import androidx.compose.animation.core.keyframes
 import androidx.compose.animation.core.spring
 import androidx.compose.animation.core.transitionDefinition
-import androidx.annotation.Sampled
 
 private val radius = FloatPropKey()
 private val alpha = FloatPropKey()
diff --git a/ui/ui-animation-core/samples/src/main/java/androidx/compose/animation/core/samples/TransitionSpecSamples.kt b/compose/animation/animation-core/samples/src/main/java/androidx/compose/animation/core/samples/TransitionSpecSamples.kt
similarity index 99%
rename from ui/ui-animation-core/samples/src/main/java/androidx/compose/animation/core/samples/TransitionSpecSamples.kt
rename to compose/animation/animation-core/samples/src/main/java/androidx/compose/animation/core/samples/TransitionSpecSamples.kt
index c9e198f6..4f3e7c0 100644
--- a/ui/ui-animation-core/samples/src/main/java/androidx/compose/animation/core/samples/TransitionSpecSamples.kt
+++ b/compose/animation/animation-core/samples/src/main/java/androidx/compose/animation/core/samples/TransitionSpecSamples.kt
@@ -16,6 +16,7 @@
 
 package androidx.compose.animation.core.samples
 
+import androidx.annotation.Sampled
 import androidx.compose.animation.core.FloatPropKey
 import androidx.compose.animation.core.InterruptionHandling
 import androidx.compose.animation.core.LinearEasing
@@ -23,7 +24,6 @@
 import androidx.compose.animation.core.spring
 import androidx.compose.animation.core.transitionDefinition
 import androidx.compose.animation.core.tween
-import androidx.annotation.Sampled
 
 enum class ButtonState {
     Released,
diff --git a/ui/ui-animation-core/src/androidMain/AndroidManifest.xml b/compose/animation/animation-core/src/androidMain/AndroidManifest.xml
similarity index 100%
rename from ui/ui-animation-core/src/androidMain/AndroidManifest.xml
rename to compose/animation/animation-core/src/androidMain/AndroidManifest.xml
diff --git a/ui/ui-animation-core/src/androidMain/kotlin/androidx/compose/animation/core/AndroidAnimationClock.kt b/compose/animation/animation-core/src/androidMain/kotlin/androidx/compose/animation/core/AndroidAnimationClock.kt
similarity index 100%
rename from ui/ui-animation-core/src/androidMain/kotlin/androidx/compose/animation/core/AndroidAnimationClock.kt
rename to compose/animation/animation-core/src/androidMain/kotlin/androidx/compose/animation/core/AndroidAnimationClock.kt
diff --git a/ui/ui-animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimatedValue.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimatedValue.kt
similarity index 100%
rename from ui/ui-animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimatedValue.kt
rename to compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimatedValue.kt
diff --git a/ui/ui-animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Animation.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Animation.kt
similarity index 100%
rename from ui/ui-animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Animation.kt
rename to compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Animation.kt
diff --git a/ui/ui-animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimationClock.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimationClock.kt
similarity index 100%
rename from ui/ui-animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimationClock.kt
rename to compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimationClock.kt
diff --git a/ui/ui-animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimationClockObservable.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimationClockObservable.kt
similarity index 100%
rename from ui/ui-animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimationClockObservable.kt
rename to compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimationClockObservable.kt
diff --git a/ui/ui-animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimationSpec.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimationSpec.kt
similarity index 99%
rename from ui/ui-animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimationSpec.kt
rename to compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimationSpec.kt
index 16966c6..97cdf37 100644
--- a/ui/ui-animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimationSpec.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimationSpec.kt
@@ -17,6 +17,7 @@
 package androidx.compose.animation.core
 
 import androidx.compose.animation.core.AnimationConstants.DefaultDurationMillis
+import androidx.compose.animation.core.KeyframesSpec.KeyframesSpecConfig
 import androidx.compose.runtime.Immutable
 import androidx.compose.ui.util.annotation.IntRange
 
diff --git a/ui/ui-animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimationVectors.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimationVectors.kt
similarity index 100%
rename from ui/ui-animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimationVectors.kt
rename to compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimationVectors.kt
diff --git a/ui/ui-animation-core/src/commonMain/kotlin/androidx/compose/animation/core/ComplexDouble.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/ComplexDouble.kt
similarity index 100%
rename from ui/ui-animation-core/src/commonMain/kotlin/androidx/compose/animation/core/ComplexDouble.kt
rename to compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/ComplexDouble.kt
diff --git a/ui/ui-animation-core/src/commonMain/kotlin/androidx/compose/animation/core/DynamicTargetAnimation.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/DynamicTargetAnimation.kt
similarity index 100%
rename from ui/ui-animation-core/src/commonMain/kotlin/androidx/compose/animation/core/DynamicTargetAnimation.kt
rename to compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/DynamicTargetAnimation.kt
diff --git a/ui/ui-animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Easing.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Easing.kt
similarity index 100%
rename from ui/ui-animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Easing.kt
rename to compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Easing.kt
diff --git a/ui/ui-animation-core/src/commonMain/kotlin/androidx/compose/animation/core/FloatAnimationSpec.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/FloatAnimationSpec.kt
similarity index 100%
rename from ui/ui-animation-core/src/commonMain/kotlin/androidx/compose/animation/core/FloatAnimationSpec.kt
rename to compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/FloatAnimationSpec.kt
diff --git a/ui/ui-animation-core/src/commonMain/kotlin/androidx/compose/animation/core/FloatDecayAnimationSpec.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/FloatDecayAnimationSpec.kt
similarity index 100%
rename from ui/ui-animation-core/src/commonMain/kotlin/androidx/compose/animation/core/FloatDecayAnimationSpec.kt
rename to compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/FloatDecayAnimationSpec.kt
diff --git a/ui/ui-animation-core/src/commonMain/kotlin/androidx/compose/animation/core/ManualFrameClock.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/ManualFrameClock.kt
similarity index 100%
rename from ui/ui-animation-core/src/commonMain/kotlin/androidx/compose/animation/core/ManualFrameClock.kt
rename to compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/ManualFrameClock.kt
diff --git a/ui/ui-animation-core/src/commonMain/kotlin/androidx/compose/animation/core/MonotonicFrameAnimationClock.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/MonotonicFrameAnimationClock.kt
similarity index 100%
rename from ui/ui-animation-core/src/commonMain/kotlin/androidx/compose/animation/core/MonotonicFrameAnimationClock.kt
rename to compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/MonotonicFrameAnimationClock.kt
diff --git a/ui/ui-animation-core/src/commonMain/kotlin/androidx/compose/animation/core/PropKey.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/PropKey.kt
similarity index 100%
rename from ui/ui-animation-core/src/commonMain/kotlin/androidx/compose/animation/core/PropKey.kt
rename to compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/PropKey.kt
diff --git a/ui/ui-animation-core/src/commonMain/kotlin/androidx/compose/animation/core/SpringEstimation.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/SpringEstimation.kt
similarity index 100%
rename from ui/ui-animation-core/src/commonMain/kotlin/androidx/compose/animation/core/SpringEstimation.kt
rename to compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/SpringEstimation.kt
diff --git a/ui/ui-animation-core/src/commonMain/kotlin/androidx/compose/animation/core/SpringSimulation.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/SpringSimulation.kt
similarity index 100%
rename from ui/ui-animation-core/src/commonMain/kotlin/androidx/compose/animation/core/SpringSimulation.kt
rename to compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/SpringSimulation.kt
diff --git a/ui/ui-animation-core/src/commonMain/kotlin/androidx/compose/animation/core/ToolingGlue.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/ToolingGlue.kt
similarity index 100%
rename from ui/ui-animation-core/src/commonMain/kotlin/androidx/compose/animation/core/ToolingGlue.kt
rename to compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/ToolingGlue.kt
diff --git a/ui/ui-animation-core/src/commonMain/kotlin/androidx/compose/animation/core/TransitionAnimation.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/TransitionAnimation.kt
similarity index 100%
rename from ui/ui-animation-core/src/commonMain/kotlin/androidx/compose/animation/core/TransitionAnimation.kt
rename to compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/TransitionAnimation.kt
diff --git a/ui/ui-animation-core/src/commonMain/kotlin/androidx/compose/animation/core/TransitionDefinition.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/TransitionDefinition.kt
similarity index 100%
rename from ui/ui-animation-core/src/commonMain/kotlin/androidx/compose/animation/core/TransitionDefinition.kt
rename to compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/TransitionDefinition.kt
diff --git a/ui/ui-animation-core/src/commonMain/kotlin/androidx/compose/animation/core/TransitionState.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/TransitionState.kt
similarity index 100%
rename from ui/ui-animation-core/src/commonMain/kotlin/androidx/compose/animation/core/TransitionState.kt
rename to compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/TransitionState.kt
diff --git a/ui/ui-animation-core/src/commonMain/kotlin/androidx/compose/animation/core/VectorizedAnimationSpec.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/VectorizedAnimationSpec.kt
similarity index 100%
rename from ui/ui-animation-core/src/commonMain/kotlin/androidx/compose/animation/core/VectorizedAnimationSpec.kt
rename to compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/VectorizedAnimationSpec.kt
diff --git a/ui/ui-animation-core/src/desktopMain/kotlin/androidx/compose/animation/core/DefaultAnimationClock.kt b/compose/animation/animation-core/src/desktopMain/kotlin/androidx/compose/animation/core/DefaultAnimationClock.kt
similarity index 81%
rename from ui/ui-animation-core/src/desktopMain/kotlin/androidx/compose/animation/core/DefaultAnimationClock.kt
rename to compose/animation/animation-core/src/desktopMain/kotlin/androidx/compose/animation/core/DefaultAnimationClock.kt
index a9dca06..eef5dd2 100644
--- a/ui/ui-animation-core/src/desktopMain/kotlin/androidx/compose/animation/core/DefaultAnimationClock.kt
+++ b/compose/animation/animation-core/src/desktopMain/kotlin/androidx/compose/animation/core/DefaultAnimationClock.kt
@@ -38,23 +38,20 @@
     }
 
     override fun subscribe(observer: AnimationClockObserver) {
+        if (!scheduled) {
+            dispatcher.scheduleCallbackWithDelay(delay, ::frameCallback)
+            scheduled = true
+        }
         super.subscribe(observer)
-        scheduleIfNeeded()
     }
 
     override fun dispatchTime(frameTimeMillis: Long) {
         super.dispatchTime(frameTimeMillis)
-        scheduleIfNeeded()
-    }
-
-    private fun scheduleIfNeeded() {
-        when {
-            scheduled -> return
-            !hasObservers() -> return
-            else -> {
-                scheduled = true
-                dispatcher.scheduleCallbackWithDelay(delay, ::frameCallback)
-            }
+        scheduled = if (hasObservers()) {
+            dispatcher.scheduleCallbackWithDelay(delay, ::frameCallback)
+            true
+        } else {
+            false
         }
     }
 }
\ No newline at end of file
diff --git a/ui/ui-animation-core/src/test/java/androidx/compose/animation/core/AnimatedValueTest.kt b/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/AnimatedValueTest.kt
similarity index 100%
rename from ui/ui-animation-core/src/test/java/androidx/compose/animation/core/AnimatedValueTest.kt
rename to compose/animation/animation-core/src/test/java/androidx/compose/animation/core/AnimatedValueTest.kt
diff --git a/ui/ui-animation-core/src/test/java/androidx/compose/animation/core/AnimationClockTest.kt b/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/AnimationClockTest.kt
similarity index 100%
rename from ui/ui-animation-core/src/test/java/androidx/compose/animation/core/AnimationClockTest.kt
rename to compose/animation/animation-core/src/test/java/androidx/compose/animation/core/AnimationClockTest.kt
diff --git a/ui/ui-animation-core/src/test/java/androidx/compose/animation/core/AnimationTest.kt b/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/AnimationTest.kt
similarity index 100%
rename from ui/ui-animation-core/src/test/java/androidx/compose/animation/core/AnimationTest.kt
rename to compose/animation/animation-core/src/test/java/androidx/compose/animation/core/AnimationTest.kt
diff --git a/ui/ui-animation-core/src/test/java/androidx/compose/animation/core/AnimationTestUtils.kt b/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/AnimationTestUtils.kt
similarity index 100%
rename from ui/ui-animation-core/src/test/java/androidx/compose/animation/core/AnimationTestUtils.kt
rename to compose/animation/animation-core/src/test/java/androidx/compose/animation/core/AnimationTestUtils.kt
diff --git a/ui/ui-animation-core/src/test/java/androidx/compose/animation/core/AnimationVectorTest.kt b/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/AnimationVectorTest.kt
similarity index 100%
rename from ui/ui-animation-core/src/test/java/androidx/compose/animation/core/AnimationVectorTest.kt
rename to compose/animation/animation-core/src/test/java/androidx/compose/animation/core/AnimationVectorTest.kt
diff --git a/ui/ui-animation-core/src/test/java/androidx/compose/animation/core/DecayAnimationTest.kt b/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/DecayAnimationTest.kt
similarity index 100%
rename from ui/ui-animation-core/src/test/java/androidx/compose/animation/core/DecayAnimationTest.kt
rename to compose/animation/animation-core/src/test/java/androidx/compose/animation/core/DecayAnimationTest.kt
diff --git a/ui/ui-animation-core/src/test/java/androidx/compose/animation/core/EasingTest.kt b/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/EasingTest.kt
similarity index 100%
rename from ui/ui-animation-core/src/test/java/androidx/compose/animation/core/EasingTest.kt
rename to compose/animation/animation-core/src/test/java/androidx/compose/animation/core/EasingTest.kt
diff --git a/ui/ui-animation-core/src/test/java/androidx/compose/animation/core/KeyframeAnimationTest.kt b/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/KeyframeAnimationTest.kt
similarity index 100%
rename from ui/ui-animation-core/src/test/java/androidx/compose/animation/core/KeyframeAnimationTest.kt
rename to compose/animation/animation-core/src/test/java/androidx/compose/animation/core/KeyframeAnimationTest.kt
diff --git a/ui/ui-animation-core/src/test/java/androidx/compose/animation/core/MotionTest.kt b/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/MotionTest.kt
similarity index 100%
rename from ui/ui-animation-core/src/test/java/androidx/compose/animation/core/MotionTest.kt
rename to compose/animation/animation-core/src/test/java/androidx/compose/animation/core/MotionTest.kt
diff --git a/ui/ui-animation-core/src/test/java/androidx/compose/animation/core/PhysicsAnimationTest.kt b/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/PhysicsAnimationTest.kt
similarity index 100%
rename from ui/ui-animation-core/src/test/java/androidx/compose/animation/core/PhysicsAnimationTest.kt
rename to compose/animation/animation-core/src/test/java/androidx/compose/animation/core/PhysicsAnimationTest.kt
diff --git a/ui/ui-animation-core/src/test/java/androidx/compose/animation/core/RepeatableAnimationTest.kt b/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/RepeatableAnimationTest.kt
similarity index 100%
rename from ui/ui-animation-core/src/test/java/androidx/compose/animation/core/RepeatableAnimationTest.kt
rename to compose/animation/animation-core/src/test/java/androidx/compose/animation/core/RepeatableAnimationTest.kt
diff --git a/ui/ui-animation-core/src/test/java/androidx/compose/animation/core/SnapAnimationTest.kt b/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/SnapAnimationTest.kt
similarity index 100%
rename from ui/ui-animation-core/src/test/java/androidx/compose/animation/core/SnapAnimationTest.kt
rename to compose/animation/animation-core/src/test/java/androidx/compose/animation/core/SnapAnimationTest.kt
diff --git a/ui/ui-animation-core/src/test/java/androidx/compose/animation/core/SpringEstimationTest.kt b/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/SpringEstimationTest.kt
similarity index 100%
rename from ui/ui-animation-core/src/test/java/androidx/compose/animation/core/SpringEstimationTest.kt
rename to compose/animation/animation-core/src/test/java/androidx/compose/animation/core/SpringEstimationTest.kt
diff --git a/ui/ui-animation-core/src/test/java/androidx/compose/animation/core/ToolingGlueTest.kt b/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/ToolingGlueTest.kt
similarity index 100%
rename from ui/ui-animation-core/src/test/java/androidx/compose/animation/core/ToolingGlueTest.kt
rename to compose/animation/animation-core/src/test/java/androidx/compose/animation/core/ToolingGlueTest.kt
diff --git a/ui/ui-animation-core/src/test/java/androidx/compose/animation/core/TransitionAnimationTest.kt b/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/TransitionAnimationTest.kt
similarity index 100%
rename from ui/ui-animation-core/src/test/java/androidx/compose/animation/core/TransitionAnimationTest.kt
rename to compose/animation/animation-core/src/test/java/androidx/compose/animation/core/TransitionAnimationTest.kt
diff --git a/ui/ui-animation-core/src/test/java/androidx/compose/animation/core/TransitionDefinitionTest.kt b/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/TransitionDefinitionTest.kt
similarity index 100%
rename from ui/ui-animation-core/src/test/java/androidx/compose/animation/core/TransitionDefinitionTest.kt
rename to compose/animation/animation-core/src/test/java/androidx/compose/animation/core/TransitionDefinitionTest.kt
diff --git a/ui/ui-animation-core/src/test/java/androidx/compose/animation/core/TweenAnimationTest.kt b/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/TweenAnimationTest.kt
similarity index 100%
rename from ui/ui-animation-core/src/test/java/androidx/compose/animation/core/TweenAnimationTest.kt
rename to compose/animation/animation-core/src/test/java/androidx/compose/animation/core/TweenAnimationTest.kt
diff --git a/ui/ui-animation-core/src/test/java/androidx/compose/animation/core/TypeConverterTest.kt b/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/TypeConverterTest.kt
similarity index 100%
rename from ui/ui-animation-core/src/test/java/androidx/compose/animation/core/TypeConverterTest.kt
rename to compose/animation/animation-core/src/test/java/androidx/compose/animation/core/TypeConverterTest.kt
diff --git a/ui/ui-animation/OWNERS b/compose/animation/animation/OWNERS
similarity index 100%
rename from ui/ui-animation/OWNERS
rename to compose/animation/animation/OWNERS
diff --git a/ui/ui-animation/api/0.1.0-dev01.txt b/compose/animation/animation/api/0.1.0-dev01.txt
similarity index 100%
rename from ui/ui-animation/api/0.1.0-dev01.txt
rename to compose/animation/animation/api/0.1.0-dev01.txt
diff --git a/ui/ui-animation/api/0.1.0-dev02.txt b/compose/animation/animation/api/0.1.0-dev02.txt
similarity index 100%
rename from ui/ui-animation/api/0.1.0-dev02.txt
rename to compose/animation/animation/api/0.1.0-dev02.txt
diff --git a/ui/ui-animation/api/0.1.0-dev03.txt b/compose/animation/animation/api/0.1.0-dev03.txt
similarity index 100%
rename from ui/ui-animation/api/0.1.0-dev03.txt
rename to compose/animation/animation/api/0.1.0-dev03.txt
diff --git a/ui/ui-animation/api/0.1.0-dev04.txt b/compose/animation/animation/api/0.1.0-dev04.txt
similarity index 100%
rename from ui/ui-animation/api/0.1.0-dev04.txt
rename to compose/animation/animation/api/0.1.0-dev04.txt
diff --git a/ui/ui-animation/api/0.1.0-dev05.txt b/compose/animation/animation/api/0.1.0-dev05.txt
similarity index 100%
rename from ui/ui-animation/api/0.1.0-dev05.txt
rename to compose/animation/animation/api/0.1.0-dev05.txt
diff --git a/ui/ui-animation/api/0.1.0-dev06.txt b/compose/animation/animation/api/0.1.0-dev06.txt
similarity index 100%
rename from ui/ui-animation/api/0.1.0-dev06.txt
rename to compose/animation/animation/api/0.1.0-dev06.txt
diff --git a/ui/ui-animation/api/0.1.0-dev07.txt b/compose/animation/animation/api/0.1.0-dev07.txt
similarity index 100%
rename from ui/ui-animation/api/0.1.0-dev07.txt
rename to compose/animation/animation/api/0.1.0-dev07.txt
diff --git a/ui/ui-animation/api/0.1.0-dev08.txt b/compose/animation/animation/api/0.1.0-dev08.txt
similarity index 100%
rename from ui/ui-animation/api/0.1.0-dev08.txt
rename to compose/animation/animation/api/0.1.0-dev08.txt
diff --git a/ui/ui-animation/api/0.1.0-dev09.txt b/compose/animation/animation/api/0.1.0-dev09.txt
similarity index 100%
rename from ui/ui-animation/api/0.1.0-dev09.txt
rename to compose/animation/animation/api/0.1.0-dev09.txt
diff --git a/ui/ui-animation/api/0.1.0-dev10.txt b/compose/animation/animation/api/0.1.0-dev10.txt
similarity index 100%
rename from ui/ui-animation/api/0.1.0-dev10.txt
rename to compose/animation/animation/api/0.1.0-dev10.txt
diff --git a/ui/ui-animation/api/0.1.0-dev11.txt b/compose/animation/animation/api/0.1.0-dev11.txt
similarity index 100%
rename from ui/ui-animation/api/0.1.0-dev11.txt
rename to compose/animation/animation/api/0.1.0-dev11.txt
diff --git a/ui/ui-animation/api/0.1.0-dev12.txt b/compose/animation/animation/api/0.1.0-dev12.txt
similarity index 100%
rename from ui/ui-animation/api/0.1.0-dev12.txt
rename to compose/animation/animation/api/0.1.0-dev12.txt
diff --git a/ui/ui-animation/api/0.1.0-dev14.txt b/compose/animation/animation/api/0.1.0-dev14.txt
similarity index 100%
rename from ui/ui-animation/api/0.1.0-dev14.txt
rename to compose/animation/animation/api/0.1.0-dev14.txt
diff --git a/ui/ui-animation/api/0.1.0-dev15.txt b/compose/animation/animation/api/0.1.0-dev15.txt
similarity index 100%
rename from ui/ui-animation/api/0.1.0-dev15.txt
rename to compose/animation/animation/api/0.1.0-dev15.txt
diff --git a/ui/ui-animation/api/0.1.0-dev16.txt b/compose/animation/animation/api/0.1.0-dev16.txt
similarity index 100%
rename from ui/ui-animation/api/0.1.0-dev16.txt
rename to compose/animation/animation/api/0.1.0-dev16.txt
diff --git a/ui/ui-animation/api/api_lint.ignore b/compose/animation/animation/api/api_lint.ignore
similarity index 100%
rename from ui/ui-animation/api/api_lint.ignore
rename to compose/animation/animation/api/api_lint.ignore
diff --git a/ui/ui-animation/api/current.txt b/compose/animation/animation/api/current.txt
similarity index 100%
rename from ui/ui-animation/api/current.txt
rename to compose/animation/animation/api/current.txt
diff --git a/ui/ui-animation/api/public_plus_experimental_0.1.0-dev01.txt b/compose/animation/animation/api/public_plus_experimental_0.1.0-dev01.txt
similarity index 100%
rename from ui/ui-animation/api/public_plus_experimental_0.1.0-dev01.txt
rename to compose/animation/animation/api/public_plus_experimental_0.1.0-dev01.txt
diff --git a/ui/ui-animation/api/public_plus_experimental_0.1.0-dev02.txt b/compose/animation/animation/api/public_plus_experimental_0.1.0-dev02.txt
similarity index 100%
rename from ui/ui-animation/api/public_plus_experimental_0.1.0-dev02.txt
rename to compose/animation/animation/api/public_plus_experimental_0.1.0-dev02.txt
diff --git a/ui/ui-animation/api/public_plus_experimental_0.1.0-dev03.txt b/compose/animation/animation/api/public_plus_experimental_0.1.0-dev03.txt
similarity index 100%
rename from ui/ui-animation/api/public_plus_experimental_0.1.0-dev03.txt
rename to compose/animation/animation/api/public_plus_experimental_0.1.0-dev03.txt
diff --git a/ui/ui-animation/api/public_plus_experimental_0.1.0-dev04.txt b/compose/animation/animation/api/public_plus_experimental_0.1.0-dev04.txt
similarity index 100%
rename from ui/ui-animation/api/public_plus_experimental_0.1.0-dev04.txt
rename to compose/animation/animation/api/public_plus_experimental_0.1.0-dev04.txt
diff --git a/ui/ui-animation/api/public_plus_experimental_0.1.0-dev05.txt b/compose/animation/animation/api/public_plus_experimental_0.1.0-dev05.txt
similarity index 100%
rename from ui/ui-animation/api/public_plus_experimental_0.1.0-dev05.txt
rename to compose/animation/animation/api/public_plus_experimental_0.1.0-dev05.txt
diff --git a/ui/ui-animation/api/public_plus_experimental_0.1.0-dev06.txt b/compose/animation/animation/api/public_plus_experimental_0.1.0-dev06.txt
similarity index 100%
rename from ui/ui-animation/api/public_plus_experimental_0.1.0-dev06.txt
rename to compose/animation/animation/api/public_plus_experimental_0.1.0-dev06.txt
diff --git a/ui/ui-animation/api/public_plus_experimental_0.1.0-dev07.txt b/compose/animation/animation/api/public_plus_experimental_0.1.0-dev07.txt
similarity index 100%
rename from ui/ui-animation/api/public_plus_experimental_0.1.0-dev07.txt
rename to compose/animation/animation/api/public_plus_experimental_0.1.0-dev07.txt
diff --git a/ui/ui-animation/api/public_plus_experimental_0.1.0-dev08.txt b/compose/animation/animation/api/public_plus_experimental_0.1.0-dev08.txt
similarity index 100%
rename from ui/ui-animation/api/public_plus_experimental_0.1.0-dev08.txt
rename to compose/animation/animation/api/public_plus_experimental_0.1.0-dev08.txt
diff --git a/ui/ui-animation/api/public_plus_experimental_0.1.0-dev09.txt b/compose/animation/animation/api/public_plus_experimental_0.1.0-dev09.txt
similarity index 100%
rename from ui/ui-animation/api/public_plus_experimental_0.1.0-dev09.txt
rename to compose/animation/animation/api/public_plus_experimental_0.1.0-dev09.txt
diff --git a/ui/ui-animation/api/public_plus_experimental_0.1.0-dev10.txt b/compose/animation/animation/api/public_plus_experimental_0.1.0-dev10.txt
similarity index 100%
rename from ui/ui-animation/api/public_plus_experimental_0.1.0-dev10.txt
rename to compose/animation/animation/api/public_plus_experimental_0.1.0-dev10.txt
diff --git a/ui/ui-animation/api/public_plus_experimental_0.1.0-dev11.txt b/compose/animation/animation/api/public_plus_experimental_0.1.0-dev11.txt
similarity index 100%
rename from ui/ui-animation/api/public_plus_experimental_0.1.0-dev11.txt
rename to compose/animation/animation/api/public_plus_experimental_0.1.0-dev11.txt
diff --git a/ui/ui-animation/api/public_plus_experimental_0.1.0-dev12.txt b/compose/animation/animation/api/public_plus_experimental_0.1.0-dev12.txt
similarity index 100%
rename from ui/ui-animation/api/public_plus_experimental_0.1.0-dev12.txt
rename to compose/animation/animation/api/public_plus_experimental_0.1.0-dev12.txt
diff --git a/ui/ui-animation/api/public_plus_experimental_0.1.0-dev14.txt b/compose/animation/animation/api/public_plus_experimental_0.1.0-dev14.txt
similarity index 100%
rename from ui/ui-animation/api/public_plus_experimental_0.1.0-dev14.txt
rename to compose/animation/animation/api/public_plus_experimental_0.1.0-dev14.txt
diff --git a/ui/ui-animation/api/public_plus_experimental_0.1.0-dev15.txt b/compose/animation/animation/api/public_plus_experimental_0.1.0-dev15.txt
similarity index 100%
rename from ui/ui-animation/api/public_plus_experimental_0.1.0-dev15.txt
rename to compose/animation/animation/api/public_plus_experimental_0.1.0-dev15.txt
diff --git a/ui/ui-animation/api/public_plus_experimental_0.1.0-dev16.txt b/compose/animation/animation/api/public_plus_experimental_0.1.0-dev16.txt
similarity index 100%
rename from ui/ui-animation/api/public_plus_experimental_0.1.0-dev16.txt
rename to compose/animation/animation/api/public_plus_experimental_0.1.0-dev16.txt
diff --git a/ui/ui-animation/api/public_plus_experimental_current.txt b/compose/animation/animation/api/public_plus_experimental_current.txt
similarity index 100%
rename from ui/ui-animation/api/public_plus_experimental_current.txt
rename to compose/animation/animation/api/public_plus_experimental_current.txt
diff --git a/ui/ui-animation/api/res-0.1.0-dev01.txt b/compose/animation/animation/api/res-0.1.0-dev01.txt
similarity index 100%
rename from ui/ui-animation/api/res-0.1.0-dev01.txt
rename to compose/animation/animation/api/res-0.1.0-dev01.txt
diff --git a/ui/ui-animation/api/res-0.1.0-dev02.txt b/compose/animation/animation/api/res-0.1.0-dev02.txt
similarity index 100%
rename from ui/ui-animation/api/res-0.1.0-dev02.txt
rename to compose/animation/animation/api/res-0.1.0-dev02.txt
diff --git a/ui/ui-animation/api/res-0.1.0-dev03.txt b/compose/animation/animation/api/res-0.1.0-dev03.txt
similarity index 100%
rename from ui/ui-animation/api/res-0.1.0-dev03.txt
rename to compose/animation/animation/api/res-0.1.0-dev03.txt
diff --git a/compose/compose-runtime/api/res-0.1.0-dev04.txt b/compose/animation/animation/api/res-0.1.0-dev04.txt
similarity index 100%
copy from compose/compose-runtime/api/res-0.1.0-dev04.txt
copy to compose/animation/animation/api/res-0.1.0-dev04.txt
diff --git a/compose/compose-runtime/api/res-0.1.0-dev05.txt b/compose/animation/animation/api/res-0.1.0-dev05.txt
similarity index 100%
copy from compose/compose-runtime/api/res-0.1.0-dev05.txt
copy to compose/animation/animation/api/res-0.1.0-dev05.txt
diff --git a/compose/compose-runtime/api/res-0.1.0-dev06.txt b/compose/animation/animation/api/res-0.1.0-dev06.txt
similarity index 100%
copy from compose/compose-runtime/api/res-0.1.0-dev06.txt
copy to compose/animation/animation/api/res-0.1.0-dev06.txt
diff --git a/compose/compose-runtime/api/res-0.1.0-dev07.txt b/compose/animation/animation/api/res-0.1.0-dev07.txt
similarity index 100%
copy from compose/compose-runtime/api/res-0.1.0-dev07.txt
copy to compose/animation/animation/api/res-0.1.0-dev07.txt
diff --git a/compose/compose-runtime/api/res-0.1.0-dev08.txt b/compose/animation/animation/api/res-0.1.0-dev08.txt
similarity index 100%
copy from compose/compose-runtime/api/res-0.1.0-dev08.txt
copy to compose/animation/animation/api/res-0.1.0-dev08.txt
diff --git a/compose/compose-runtime/api/res-0.1.0-dev09.txt b/compose/animation/animation/api/res-0.1.0-dev09.txt
similarity index 100%
copy from compose/compose-runtime/api/res-0.1.0-dev09.txt
copy to compose/animation/animation/api/res-0.1.0-dev09.txt
diff --git a/compose/compose-runtime/api/res-0.1.0-dev10.txt b/compose/animation/animation/api/res-0.1.0-dev10.txt
similarity index 100%
copy from compose/compose-runtime/api/res-0.1.0-dev10.txt
copy to compose/animation/animation/api/res-0.1.0-dev10.txt
diff --git a/compose/compose-runtime/api/res-0.1.0-dev11.txt b/compose/animation/animation/api/res-0.1.0-dev11.txt
similarity index 100%
copy from compose/compose-runtime/api/res-0.1.0-dev11.txt
copy to compose/animation/animation/api/res-0.1.0-dev11.txt
diff --git a/compose/compose-runtime/api/res-0.1.0-dev12.txt b/compose/animation/animation/api/res-0.1.0-dev12.txt
similarity index 100%
copy from compose/compose-runtime/api/res-0.1.0-dev12.txt
copy to compose/animation/animation/api/res-0.1.0-dev12.txt
diff --git a/compose/compose-runtime/api/res-0.1.0-dev14.txt b/compose/animation/animation/api/res-0.1.0-dev14.txt
similarity index 100%
copy from compose/compose-runtime/api/res-0.1.0-dev14.txt
copy to compose/animation/animation/api/res-0.1.0-dev14.txt
diff --git a/compose/compose-dispatch/api/res-0.1.0-dev15.txt b/compose/animation/animation/api/res-0.1.0-dev15.txt
similarity index 100%
copy from compose/compose-dispatch/api/res-0.1.0-dev15.txt
copy to compose/animation/animation/api/res-0.1.0-dev15.txt
diff --git a/compose/compose-dispatch/api/res-0.1.0-dev16.txt b/compose/animation/animation/api/res-0.1.0-dev16.txt
similarity index 100%
copy from compose/compose-dispatch/api/res-0.1.0-dev16.txt
copy to compose/animation/animation/api/res-0.1.0-dev16.txt
diff --git a/compose/compose-dispatch/api/res-current.txt b/compose/animation/animation/api/res-current.txt
similarity index 100%
copy from compose/compose-dispatch/api/res-current.txt
copy to compose/animation/animation/api/res-current.txt
diff --git a/ui/ui-animation/api/restricted_0.1.0-dev01.txt b/compose/animation/animation/api/restricted_0.1.0-dev01.txt
similarity index 100%
rename from ui/ui-animation/api/restricted_0.1.0-dev01.txt
rename to compose/animation/animation/api/restricted_0.1.0-dev01.txt
diff --git a/ui/ui-animation/api/restricted_0.1.0-dev02.txt b/compose/animation/animation/api/restricted_0.1.0-dev02.txt
similarity index 100%
rename from ui/ui-animation/api/restricted_0.1.0-dev02.txt
rename to compose/animation/animation/api/restricted_0.1.0-dev02.txt
diff --git a/ui/ui-animation/api/restricted_0.1.0-dev03.txt b/compose/animation/animation/api/restricted_0.1.0-dev03.txt
similarity index 100%
rename from ui/ui-animation/api/restricted_0.1.0-dev03.txt
rename to compose/animation/animation/api/restricted_0.1.0-dev03.txt
diff --git a/ui/ui-animation/api/restricted_0.1.0-dev04.txt b/compose/animation/animation/api/restricted_0.1.0-dev04.txt
similarity index 100%
rename from ui/ui-animation/api/restricted_0.1.0-dev04.txt
rename to compose/animation/animation/api/restricted_0.1.0-dev04.txt
diff --git a/ui/ui-animation/api/restricted_0.1.0-dev05.txt b/compose/animation/animation/api/restricted_0.1.0-dev05.txt
similarity index 100%
rename from ui/ui-animation/api/restricted_0.1.0-dev05.txt
rename to compose/animation/animation/api/restricted_0.1.0-dev05.txt
diff --git a/ui/ui-animation/api/restricted_0.1.0-dev06.txt b/compose/animation/animation/api/restricted_0.1.0-dev06.txt
similarity index 100%
rename from ui/ui-animation/api/restricted_0.1.0-dev06.txt
rename to compose/animation/animation/api/restricted_0.1.0-dev06.txt
diff --git a/ui/ui-animation/api/restricted_0.1.0-dev07.txt b/compose/animation/animation/api/restricted_0.1.0-dev07.txt
similarity index 100%
rename from ui/ui-animation/api/restricted_0.1.0-dev07.txt
rename to compose/animation/animation/api/restricted_0.1.0-dev07.txt
diff --git a/ui/ui-animation/api/restricted_0.1.0-dev08.txt b/compose/animation/animation/api/restricted_0.1.0-dev08.txt
similarity index 100%
rename from ui/ui-animation/api/restricted_0.1.0-dev08.txt
rename to compose/animation/animation/api/restricted_0.1.0-dev08.txt
diff --git a/ui/ui-animation/api/restricted_0.1.0-dev09.txt b/compose/animation/animation/api/restricted_0.1.0-dev09.txt
similarity index 100%
rename from ui/ui-animation/api/restricted_0.1.0-dev09.txt
rename to compose/animation/animation/api/restricted_0.1.0-dev09.txt
diff --git a/ui/ui-animation/api/restricted_0.1.0-dev10.txt b/compose/animation/animation/api/restricted_0.1.0-dev10.txt
similarity index 100%
rename from ui/ui-animation/api/restricted_0.1.0-dev10.txt
rename to compose/animation/animation/api/restricted_0.1.0-dev10.txt
diff --git a/ui/ui-animation/api/restricted_0.1.0-dev11.txt b/compose/animation/animation/api/restricted_0.1.0-dev11.txt
similarity index 100%
rename from ui/ui-animation/api/restricted_0.1.0-dev11.txt
rename to compose/animation/animation/api/restricted_0.1.0-dev11.txt
diff --git a/ui/ui-animation/api/restricted_0.1.0-dev12.txt b/compose/animation/animation/api/restricted_0.1.0-dev12.txt
similarity index 100%
rename from ui/ui-animation/api/restricted_0.1.0-dev12.txt
rename to compose/animation/animation/api/restricted_0.1.0-dev12.txt
diff --git a/ui/ui-animation/api/restricted_0.1.0-dev14.txt b/compose/animation/animation/api/restricted_0.1.0-dev14.txt
similarity index 100%
rename from ui/ui-animation/api/restricted_0.1.0-dev14.txt
rename to compose/animation/animation/api/restricted_0.1.0-dev14.txt
diff --git a/ui/ui-animation/api/restricted_0.1.0-dev15.txt b/compose/animation/animation/api/restricted_0.1.0-dev15.txt
similarity index 100%
rename from ui/ui-animation/api/restricted_0.1.0-dev15.txt
rename to compose/animation/animation/api/restricted_0.1.0-dev15.txt
diff --git a/ui/ui-animation/api/restricted_0.1.0-dev16.txt b/compose/animation/animation/api/restricted_0.1.0-dev16.txt
similarity index 100%
rename from ui/ui-animation/api/restricted_0.1.0-dev16.txt
rename to compose/animation/animation/api/restricted_0.1.0-dev16.txt
diff --git a/ui/ui-animation/api/restricted_current.txt b/compose/animation/animation/api/restricted_current.txt
similarity index 100%
rename from ui/ui-animation/api/restricted_current.txt
rename to compose/animation/animation/api/restricted_current.txt
diff --git a/ui/ui-animation/build.gradle b/compose/animation/animation/build.gradle
similarity index 100%
rename from ui/ui-animation/build.gradle
rename to compose/animation/animation/build.gradle
diff --git a/ui/ui-animation/integration-tests/animation-demos/build.gradle b/compose/animation/animation/integration-tests/animation-demos/build.gradle
similarity index 100%
rename from ui/ui-animation/integration-tests/animation-demos/build.gradle
rename to compose/animation/animation/integration-tests/animation-demos/build.gradle
diff --git a/ui/ui-animation/integration-tests/animation-demos/src/main/AndroidManifest.xml b/compose/animation/animation/integration-tests/animation-demos/src/main/AndroidManifest.xml
similarity index 100%
rename from ui/ui-animation/integration-tests/animation-demos/src/main/AndroidManifest.xml
rename to compose/animation/animation/integration-tests/animation-demos/src/main/AndroidManifest.xml
diff --git a/ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/AnimatableSeekBarDemo.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/AnimatableSeekBarDemo.kt
similarity index 99%
rename from ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/AnimatableSeekBarDemo.kt
rename to compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/AnimatableSeekBarDemo.kt
index 896feae..3cf03c9 100644
--- a/ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/AnimatableSeekBarDemo.kt
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/AnimatableSeekBarDemo.kt
@@ -16,33 +16,33 @@
 
 package androidx.compose.animation.demos
 
+import androidx.compose.animation.animatedFloat
 import androidx.compose.animation.core.FastOutSlowInEasing
 import androidx.compose.animation.core.FloatPropKey
 import androidx.compose.animation.core.ManualAnimationClock
 import androidx.compose.animation.core.TweenSpec
 import androidx.compose.animation.core.transitionDefinition
 import androidx.compose.animation.core.tween
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.Providers
-import androidx.compose.runtime.remember
 import androidx.compose.animation.transition
-import androidx.compose.animation.animatedFloat
-import androidx.compose.ui.platform.AnimationClockAmbient
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.gesture.DragObserver
-import androidx.compose.ui.gesture.pressIndicatorGestureFilter
-import androidx.compose.ui.gesture.rawDragGestureFilter
 import androidx.compose.foundation.Box
 import androidx.compose.foundation.Canvas
 import androidx.compose.foundation.Text
-import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.geometry.Size
-import androidx.compose.ui.graphics.Color
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.preferredHeight
 import androidx.compose.foundation.layout.preferredSize
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.Providers
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.gesture.DragObserver
+import androidx.compose.ui.gesture.pressIndicatorGestureFilter
+import androidx.compose.ui.gesture.rawDragGestureFilter
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.AnimationClockAmbient
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.sp
 
diff --git a/ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/AnimateContentSizeDemo.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/AnimateContentSizeDemo.kt
similarity index 99%
rename from ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/AnimateContentSizeDemo.kt
rename to compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/AnimateContentSizeDemo.kt
index 4c9869a..2e16ef8 100644
--- a/ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/AnimateContentSizeDemo.kt
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/AnimateContentSizeDemo.kt
@@ -16,8 +16,8 @@
 
 package androidx.compose.animation.demos
 
+import androidx.compose.animation.animateContentSize
 import androidx.compose.animation.core.tween
-import androidx.compose.runtime.Composable
 import androidx.compose.foundation.Box
 import androidx.compose.foundation.Text
 import androidx.compose.foundation.background
@@ -33,14 +33,14 @@
 import androidx.compose.foundation.layout.wrapContentHeight
 import androidx.compose.foundation.layout.wrapContentSize
 import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.Button
+import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
-import androidx.compose.animation.animateContentSize
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
-import androidx.compose.material.Button
 import androidx.compose.ui.unit.dp
 
 @Composable
diff --git a/ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/AnimatedVisibilityDemo.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/AnimatedVisibilityDemo.kt
similarity index 100%
rename from ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/AnimatedVisibilityDemo.kt
rename to compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/AnimatedVisibilityDemo.kt
diff --git a/ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/AnimatedVisiblilityLazyColumnDemo.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/AnimatedVisiblilityLazyColumnDemo.kt
similarity index 100%
rename from ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/AnimatedVisiblilityLazyColumnDemo.kt
rename to compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/AnimatedVisiblilityLazyColumnDemo.kt
diff --git a/ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/AnimationDemos.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/AnimationDemos.kt
similarity index 100%
rename from ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/AnimationDemos.kt
rename to compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/AnimationDemos.kt
diff --git a/ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/CrossfadeDemo.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/CrossfadeDemo.kt
similarity index 100%
rename from ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/CrossfadeDemo.kt
rename to compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/CrossfadeDemo.kt
diff --git a/ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/EnterExitTransitionDemo.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/EnterExitTransitionDemo.kt
similarity index 100%
rename from ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/EnterExitTransitionDemo.kt
rename to compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/EnterExitTransitionDemo.kt
diff --git a/ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/FancyScrollingDemo.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/FancyScrollingDemo.kt
similarity index 100%
rename from ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/FancyScrollingDemo.kt
rename to compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/FancyScrollingDemo.kt
diff --git a/ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/GestureBasedAnimationDemo.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/GestureBasedAnimationDemo.kt
similarity index 100%
rename from ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/GestureBasedAnimationDemo.kt
rename to compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/GestureBasedAnimationDemo.kt
diff --git a/ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/MultiDimensionalAnimationDemo.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/MultiDimensionalAnimationDemo.kt
similarity index 100%
rename from ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/MultiDimensionalAnimationDemo.kt
rename to compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/MultiDimensionalAnimationDemo.kt
diff --git a/ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/RepeatedRotationDemo.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/RepeatedRotationDemo.kt
similarity index 100%
rename from ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/RepeatedRotationDemo.kt
rename to compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/RepeatedRotationDemo.kt
diff --git a/ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/SingleValueAnimationDemo.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/SingleValueAnimationDemo.kt
similarity index 100%
rename from ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/SingleValueAnimationDemo.kt
rename to compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/SingleValueAnimationDemo.kt
diff --git a/ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/SpringBackScrollingDemo.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/SpringBackScrollingDemo.kt
similarity index 100%
rename from ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/SpringBackScrollingDemo.kt
rename to compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/SpringBackScrollingDemo.kt
diff --git a/ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/StateAnimationWithInterruptionsDemo.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/StateAnimationWithInterruptionsDemo.kt
similarity index 99%
rename from ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/StateAnimationWithInterruptionsDemo.kt
rename to compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/StateAnimationWithInterruptionsDemo.kt
index ac679a9..d1af27e 100644
--- a/ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/StateAnimationWithInterruptionsDemo.kt
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/StateAnimationWithInterruptionsDemo.kt
@@ -18,25 +18,25 @@
 
 import android.os.Handler
 import android.os.Looper
+import androidx.compose.animation.ColorPropKey
 import androidx.compose.animation.core.FloatPropKey
 import androidx.compose.animation.core.TransitionState
 import androidx.compose.animation.core.spring
 import androidx.compose.animation.core.transitionDefinition
 import androidx.compose.animation.core.tween
+import androidx.compose.animation.transition
+import androidx.compose.foundation.Box
+import androidx.compose.foundation.Canvas
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.setValue
-import androidx.compose.animation.ColorPropKey
-import androidx.compose.animation.transition
 import androidx.compose.ui.Modifier
-import androidx.compose.foundation.Box
-import androidx.compose.foundation.Canvas
-import androidx.compose.foundation.background
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.geometry.Size
 import androidx.compose.ui.graphics.Color
-import androidx.compose.foundation.layout.fillMaxSize
 
 @Composable
 fun StateAnimationWithInterruptionsDemo() {
diff --git a/ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/StateBasedRippleDemo.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/StateBasedRippleDemo.kt
similarity index 100%
rename from ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/StateBasedRippleDemo.kt
rename to compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/StateBasedRippleDemo.kt
diff --git a/ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/SwipeToDismissDemo.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/SwipeToDismissDemo.kt
similarity index 100%
rename from ui/ui-animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/SwipeToDismissDemo.kt
rename to compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/SwipeToDismissDemo.kt
diff --git a/ui/ui-animation/samples/build.gradle b/compose/animation/animation/samples/build.gradle
similarity index 100%
rename from ui/ui-animation/samples/build.gradle
rename to compose/animation/animation/samples/build.gradle
diff --git a/ui/ui-animation/samples/src/main/AndroidManifest.xml b/compose/animation/animation/samples/src/main/AndroidManifest.xml
similarity index 100%
rename from ui/ui-animation/samples/src/main/AndroidManifest.xml
rename to compose/animation/animation/samples/src/main/AndroidManifest.xml
diff --git a/ui/ui-animation/samples/src/main/java/androidx/compose/animation/samples/AnimatedValueSamples.kt b/compose/animation/animation/samples/src/main/java/androidx/compose/animation/samples/AnimatedValueSamples.kt
similarity index 100%
rename from ui/ui-animation/samples/src/main/java/androidx/compose/animation/samples/AnimatedValueSamples.kt
rename to compose/animation/animation/samples/src/main/java/androidx/compose/animation/samples/AnimatedValueSamples.kt
diff --git a/ui/ui-animation/samples/src/main/java/androidx/compose/animation/samples/AnimatedVisibilitySamples.kt b/compose/animation/animation/samples/src/main/java/androidx/compose/animation/samples/AnimatedVisibilitySamples.kt
similarity index 100%
rename from ui/ui-animation/samples/src/main/java/androidx/compose/animation/samples/AnimatedVisibilitySamples.kt
rename to compose/animation/animation/samples/src/main/java/androidx/compose/animation/samples/AnimatedVisibilitySamples.kt
diff --git a/ui/ui-animation/samples/src/main/java/androidx/compose/animation/samples/AnimationModifierSample.kt b/compose/animation/animation/samples/src/main/java/androidx/compose/animation/samples/AnimationModifierSample.kt
similarity index 99%
rename from ui/ui-animation/samples/src/main/java/androidx/compose/animation/samples/AnimationModifierSample.kt
rename to compose/animation/animation/samples/src/main/java/androidx/compose/animation/samples/AnimationModifierSample.kt
index f1daa01..17c1672 100644
--- a/ui/ui-animation/samples/src/main/java/androidx/compose/animation/samples/AnimationModifierSample.kt
+++ b/compose/animation/animation/samples/src/main/java/androidx/compose/animation/samples/AnimationModifierSample.kt
@@ -17,7 +17,7 @@
 package androidx.compose.animation.samples
 
 import androidx.annotation.Sampled
-import androidx.compose.runtime.Composable
+import androidx.compose.animation.animateContentSize
 import androidx.compose.foundation.Box
 import androidx.compose.foundation.Text
 import androidx.compose.foundation.background
@@ -26,11 +26,11 @@
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.wrapContentSize
 import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
-import androidx.compose.animation.animateContentSize
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.unit.dp
diff --git a/ui/ui-animation/samples/src/main/java/androidx/compose/animation/samples/CrossfadeSample.kt b/compose/animation/animation/samples/src/main/java/androidx/compose/animation/samples/CrossfadeSample.kt
similarity index 99%
rename from ui/ui-animation/samples/src/main/java/androidx/compose/animation/samples/CrossfadeSample.kt
rename to compose/animation/animation/samples/src/main/java/androidx/compose/animation/samples/CrossfadeSample.kt
index 48a7bcf..7987992 100644
--- a/ui/ui-animation/samples/src/main/java/androidx/compose/animation/samples/CrossfadeSample.kt
+++ b/compose/animation/animation/samples/src/main/java/androidx/compose/animation/samples/CrossfadeSample.kt
@@ -17,9 +17,9 @@
 package androidx.compose.animation.samples
 
 import androidx.annotation.Sampled
-import androidx.compose.runtime.Composable
 import androidx.compose.animation.Crossfade
 import androidx.compose.foundation.Text
+import androidx.compose.runtime.Composable
 
 @Sampled
 @Composable
diff --git a/ui/ui-animation/samples/src/main/java/androidx/compose/animation/samples/TransitionSamples.kt b/compose/animation/animation/samples/src/main/java/androidx/compose/animation/samples/TransitionSamples.kt
similarity index 99%
rename from ui/ui-animation/samples/src/main/java/androidx/compose/animation/samples/TransitionSamples.kt
rename to compose/animation/animation/samples/src/main/java/androidx/compose/animation/samples/TransitionSamples.kt
index 5a0c96c..0dc02f8 100644
--- a/ui/ui-animation/samples/src/main/java/androidx/compose/animation/samples/TransitionSamples.kt
+++ b/compose/animation/animation/samples/src/main/java/androidx/compose/animation/samples/TransitionSamples.kt
@@ -16,17 +16,17 @@
 
 package androidx.compose.animation.samples
 
-import androidx.compose.animation.core.transitionDefinition
 import androidx.annotation.Sampled
-import androidx.compose.runtime.Composable
 import androidx.compose.animation.ColorPropKey
 import androidx.compose.animation.DpPropKey
+import androidx.compose.animation.core.transitionDefinition
 import androidx.compose.animation.transition
-import androidx.compose.ui.Modifier
 import androidx.compose.foundation.Box
 import androidx.compose.foundation.background
-import androidx.compose.ui.graphics.Color
 import androidx.compose.foundation.layout.preferredSize
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.unit.dp
 
 private enum class State {
diff --git a/ui/ui-animation/src/androidAndroidTest/AndroidManifest.xml b/compose/animation/animation/src/androidAndroidTest/AndroidManifest.xml
similarity index 100%
rename from ui/ui-animation/src/androidAndroidTest/AndroidManifest.xml
rename to compose/animation/animation/src/androidAndroidTest/AndroidManifest.xml
diff --git a/ui/ui-animation/src/androidAndroidTest/kotlin/androidx/compose/animation/AnimatedVisibilityTest.kt b/compose/animation/animation/src/androidAndroidTest/kotlin/androidx/compose/animation/AnimatedVisibilityTest.kt
similarity index 100%
rename from ui/ui-animation/src/androidAndroidTest/kotlin/androidx/compose/animation/AnimatedVisibilityTest.kt
rename to compose/animation/animation/src/androidAndroidTest/kotlin/androidx/compose/animation/AnimatedVisibilityTest.kt
diff --git a/ui/ui-animation/src/androidAndroidTest/kotlin/androidx/compose/animation/AnimationModifierTest.kt b/compose/animation/animation/src/androidAndroidTest/kotlin/androidx/compose/animation/AnimationModifierTest.kt
similarity index 100%
rename from ui/ui-animation/src/androidAndroidTest/kotlin/androidx/compose/animation/AnimationModifierTest.kt
rename to compose/animation/animation/src/androidAndroidTest/kotlin/androidx/compose/animation/AnimationModifierTest.kt
diff --git a/ui/ui-animation/src/androidAndroidTest/kotlin/androidx/compose/animation/CrossfadeTest.kt b/compose/animation/animation/src/androidAndroidTest/kotlin/androidx/compose/animation/CrossfadeTest.kt
similarity index 100%
rename from ui/ui-animation/src/androidAndroidTest/kotlin/androidx/compose/animation/CrossfadeTest.kt
rename to compose/animation/animation/src/androidAndroidTest/kotlin/androidx/compose/animation/CrossfadeTest.kt
diff --git a/ui/ui-animation/src/androidAndroidTest/kotlin/androidx/compose/animation/SingleValueAnimationTest.kt b/compose/animation/animation/src/androidAndroidTest/kotlin/androidx/compose/animation/SingleValueAnimationTest.kt
similarity index 100%
rename from ui/ui-animation/src/androidAndroidTest/kotlin/androidx/compose/animation/SingleValueAnimationTest.kt
rename to compose/animation/animation/src/androidAndroidTest/kotlin/androidx/compose/animation/SingleValueAnimationTest.kt
diff --git a/ui/ui-animation/src/androidMain/AndroidManifest.xml b/compose/animation/animation/src/androidMain/AndroidManifest.xml
similarity index 100%
rename from ui/ui-animation/src/androidMain/AndroidManifest.xml
rename to compose/animation/animation/src/androidMain/AndroidManifest.xml
diff --git a/ui/ui-animation/src/commonMain/kotlin/androidx/compose/animation/AnimatedValueEffects.kt b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/AnimatedValueEffects.kt
similarity index 99%
rename from ui/ui-animation/src/commonMain/kotlin/androidx/compose/animation/AnimatedValueEffects.kt
rename to compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/AnimatedValueEffects.kt
index 477cc04..986f12f 100644
--- a/ui/ui-animation/src/commonMain/kotlin/androidx/compose/animation/AnimatedValueEffects.kt
+++ b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/AnimatedValueEffects.kt
@@ -30,8 +30,8 @@
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
 import androidx.compose.runtime.structuralEqualityPolicy
-import androidx.compose.ui.platform.AnimationClockAmbient
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.AnimationClockAmbient
 
 /**
  * The animatedValue effect creates an [AnimatedValue] and positionally memoizes it. When the
diff --git a/ui/ui-animation/src/commonMain/kotlin/androidx/compose/animation/AnimatedVisibility.kt b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/AnimatedVisibility.kt
similarity index 100%
rename from ui/ui-animation/src/commonMain/kotlin/androidx/compose/animation/AnimatedVisibility.kt
rename to compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/AnimatedVisibility.kt
diff --git a/ui/ui-animation/src/commonMain/kotlin/androidx/compose/animation/AnimationModifier.kt b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/AnimationModifier.kt
similarity index 99%
rename from ui/ui-animation/src/commonMain/kotlin/androidx/compose/animation/AnimationModifier.kt
rename to compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/AnimationModifier.kt
index 3d23617..e5ec767 100644
--- a/ui/ui-animation/src/commonMain/kotlin/androidx/compose/animation/AnimationModifier.kt
+++ b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/AnimationModifier.kt
@@ -23,14 +23,14 @@
 import androidx.compose.animation.core.SpringSpec
 import androidx.compose.animation.core.spring
 import androidx.compose.runtime.remember
-import androidx.compose.ui.platform.AnimationClockAmbient
-import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.LayoutModifier
 import androidx.compose.ui.Measurable
 import androidx.compose.ui.MeasureScope
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.composed
 import androidx.compose.ui.draw.clipToBounds
+import androidx.compose.ui.platform.AnimationClockAmbient
+import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.IntSize
 
 /**
diff --git a/ui/ui-animation/src/commonMain/kotlin/androidx/compose/animation/Crossfade.kt b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/Crossfade.kt
similarity index 99%
rename from ui/ui-animation/src/commonMain/kotlin/androidx/compose/animation/Crossfade.kt
rename to compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/Crossfade.kt
index 27cf621..21df8cf 100644
--- a/ui/ui-animation/src/commonMain/kotlin/androidx/compose/animation/Crossfade.kt
+++ b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/Crossfade.kt
@@ -20,6 +20,7 @@
 import androidx.compose.animation.core.AnimationEndReason
 import androidx.compose.animation.core.AnimationSpec
 import androidx.compose.animation.core.tween
+import androidx.compose.foundation.layout.Stack
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.invalidate
 import androidx.compose.runtime.key
@@ -27,7 +28,6 @@
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.drawOpacity
-import androidx.compose.foundation.layout.Stack
 import androidx.compose.ui.util.fastForEach
 
 /**
diff --git a/ui/ui-animation/src/commonMain/kotlin/androidx/compose/animation/DisposableAnimationClock.kt b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/DisposableAnimationClock.kt
similarity index 100%
rename from ui/ui-animation/src/commonMain/kotlin/androidx/compose/animation/DisposableAnimationClock.kt
rename to compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/DisposableAnimationClock.kt
diff --git a/ui/ui-animation/src/commonMain/kotlin/androidx/compose/animation/EnterExitTransition.kt b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/EnterExitTransition.kt
similarity index 100%
rename from ui/ui-animation/src/commonMain/kotlin/androidx/compose/animation/EnterExitTransition.kt
rename to compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/EnterExitTransition.kt
diff --git a/ui/ui-animation/src/commonMain/kotlin/androidx/compose/animation/LegacyTransition.kt b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/LegacyTransition.kt
similarity index 97%
rename from ui/ui-animation/src/commonMain/kotlin/androidx/compose/animation/LegacyTransition.kt
rename to compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/LegacyTransition.kt
index 6bb04b6..0360f16 100644
--- a/ui/ui-animation/src/commonMain/kotlin/androidx/compose/animation/LegacyTransition.kt
+++ b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/LegacyTransition.kt
@@ -20,8 +20,6 @@
 import androidx.compose.animation.core.TransitionDefinition
 import androidx.compose.animation.core.TransitionState
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.setValue
 import androidx.compose.ui.platform.AnimationClockAmbient
 
 /**
diff --git a/ui/ui-animation/src/commonMain/kotlin/androidx/compose/animation/PropertyKeys.kt b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/PropertyKeys.kt
similarity index 100%
rename from ui/ui-animation/src/commonMain/kotlin/androidx/compose/animation/PropertyKeys.kt
rename to compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/PropertyKeys.kt
diff --git a/ui/ui-animation/src/commonMain/kotlin/androidx/compose/animation/SingleValueAnimation.kt b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/SingleValueAnimation.kt
similarity index 99%
rename from ui/ui-animation/src/commonMain/kotlin/androidx/compose/animation/SingleValueAnimation.kt
rename to compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/SingleValueAnimation.kt
index 2a814e5..f0aaef8 100644
--- a/ui/ui-animation/src/commonMain/kotlin/androidx/compose/animation/SingleValueAnimation.kt
+++ b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/SingleValueAnimation.kt
@@ -26,11 +26,11 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.onCommit
 import androidx.compose.runtime.remember
-import androidx.compose.ui.platform.AnimationClockAmbient
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.geometry.Size
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.AnimationClockAmbient
 import androidx.compose.ui.unit.Bounds
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.IntOffset
diff --git a/ui/ui-animation/src/commonMain/kotlin/androidx/compose/animation/Transition.kt b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/Transition.kt
similarity index 100%
rename from ui/ui-animation/src/commonMain/kotlin/androidx/compose/animation/Transition.kt
rename to compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/Transition.kt
diff --git a/ui/ui-animation/src/test/kotlin/androidx/compose/animation/ConverterTest.kt b/compose/animation/animation/src/test/kotlin/androidx/compose/animation/ConverterTest.kt
similarity index 99%
rename from ui/ui-animation/src/test/kotlin/androidx/compose/animation/ConverterTest.kt
rename to compose/animation/animation/src/test/kotlin/androidx/compose/animation/ConverterTest.kt
index a13e287..7bdf8c34 100644
--- a/ui/ui-animation/src/test/kotlin/androidx/compose/animation/ConverterTest.kt
+++ b/compose/animation/animation/src/test/kotlin/androidx/compose/animation/ConverterTest.kt
@@ -19,10 +19,10 @@
 import androidx.compose.animation.core.AnimationVector1D
 import androidx.compose.animation.core.AnimationVector2D
 import androidx.compose.animation.core.AnimationVector4D
+import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.colorspace.ColorSpaces
-import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
 import junit.framework.TestCase.assertEquals
diff --git a/ui/ui-animation/src/test/kotlin/androidx/compose/animation/DisposableAnimationClockTest.kt b/compose/animation/animation/src/test/kotlin/androidx/compose/animation/DisposableAnimationClockTest.kt
similarity index 100%
rename from ui/ui-animation/src/test/kotlin/androidx/compose/animation/DisposableAnimationClockTest.kt
rename to compose/animation/animation/src/test/kotlin/androidx/compose/animation/DisposableAnimationClockTest.kt
diff --git a/ui/ui-animation/src/test/kotlin/androidx/compose/animation/PropertyKeyTest.kt b/compose/animation/animation/src/test/kotlin/androidx/compose/animation/PropertyKeyTest.kt
similarity index 100%
rename from ui/ui-animation/src/test/kotlin/androidx/compose/animation/PropertyKeyTest.kt
rename to compose/animation/animation/src/test/kotlin/androidx/compose/animation/PropertyKeyTest.kt
diff --git a/compose/animation/placeholder.txt b/compose/animation/placeholder.txt
deleted file mode 100644
index b8f7c1a..0000000
--- a/compose/animation/placeholder.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This file exists to make sure the directory this is in gets created by git, so that
-settings.gradle can point to this directory and not complain about it missing.
-
-TODO: b/160233169 remove this file once the directory structure is migrated from ui/ to here.
diff --git a/compose/runtime/placeholder.txt b/compose/runtime/placeholder.txt
deleted file mode 100644
index b8f7c1a..0000000
--- a/compose/runtime/placeholder.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This file exists to make sure the directory this is in gets created by git, so that
-settings.gradle can point to this directory and not complain about it missing.
-
-TODO: b/160233169 remove this file once the directory structure is migrated from ui/ to here.
diff --git a/compose/compose-dispatch/OWNERS b/compose/runtime/runtime-dispatch/OWNERS
similarity index 100%
rename from compose/compose-dispatch/OWNERS
rename to compose/runtime/runtime-dispatch/OWNERS
diff --git a/compose/compose-dispatch/api/0.1.0-dev15.txt b/compose/runtime/runtime-dispatch/api/0.1.0-dev15.txt
similarity index 100%
rename from compose/compose-dispatch/api/0.1.0-dev15.txt
rename to compose/runtime/runtime-dispatch/api/0.1.0-dev15.txt
diff --git a/compose/compose-dispatch/api/0.1.0-dev16.txt b/compose/runtime/runtime-dispatch/api/0.1.0-dev16.txt
similarity index 100%
rename from compose/compose-dispatch/api/0.1.0-dev16.txt
rename to compose/runtime/runtime-dispatch/api/0.1.0-dev16.txt
diff --git a/compose/compose-dispatch/api/current.txt b/compose/runtime/runtime-dispatch/api/current.txt
similarity index 100%
rename from compose/compose-dispatch/api/current.txt
rename to compose/runtime/runtime-dispatch/api/current.txt
diff --git a/compose/compose-dispatch/api/public_plus_experimental_0.1.0-dev15.txt b/compose/runtime/runtime-dispatch/api/public_plus_experimental_0.1.0-dev15.txt
similarity index 100%
rename from compose/compose-dispatch/api/public_plus_experimental_0.1.0-dev15.txt
rename to compose/runtime/runtime-dispatch/api/public_plus_experimental_0.1.0-dev15.txt
diff --git a/compose/compose-dispatch/api/public_plus_experimental_0.1.0-dev16.txt b/compose/runtime/runtime-dispatch/api/public_plus_experimental_0.1.0-dev16.txt
similarity index 100%
rename from compose/compose-dispatch/api/public_plus_experimental_0.1.0-dev16.txt
rename to compose/runtime/runtime-dispatch/api/public_plus_experimental_0.1.0-dev16.txt
diff --git a/compose/compose-dispatch/api/public_plus_experimental_current.txt b/compose/runtime/runtime-dispatch/api/public_plus_experimental_current.txt
similarity index 100%
rename from compose/compose-dispatch/api/public_plus_experimental_current.txt
rename to compose/runtime/runtime-dispatch/api/public_plus_experimental_current.txt
diff --git a/compose/compose-dispatch/api/res-0.1.0-dev15.txt b/compose/runtime/runtime-dispatch/api/res-0.1.0-dev15.txt
similarity index 100%
rename from compose/compose-dispatch/api/res-0.1.0-dev15.txt
rename to compose/runtime/runtime-dispatch/api/res-0.1.0-dev15.txt
diff --git a/compose/compose-dispatch/api/res-0.1.0-dev16.txt b/compose/runtime/runtime-dispatch/api/res-0.1.0-dev16.txt
similarity index 100%
rename from compose/compose-dispatch/api/res-0.1.0-dev16.txt
rename to compose/runtime/runtime-dispatch/api/res-0.1.0-dev16.txt
diff --git a/compose/compose-dispatch/api/res-current.txt b/compose/runtime/runtime-dispatch/api/res-current.txt
similarity index 100%
rename from compose/compose-dispatch/api/res-current.txt
rename to compose/runtime/runtime-dispatch/api/res-current.txt
diff --git a/compose/compose-dispatch/api/restricted_0.1.0-dev15.txt b/compose/runtime/runtime-dispatch/api/restricted_0.1.0-dev15.txt
similarity index 100%
rename from compose/compose-dispatch/api/restricted_0.1.0-dev15.txt
rename to compose/runtime/runtime-dispatch/api/restricted_0.1.0-dev15.txt
diff --git a/compose/compose-dispatch/api/restricted_0.1.0-dev16.txt b/compose/runtime/runtime-dispatch/api/restricted_0.1.0-dev16.txt
similarity index 100%
rename from compose/compose-dispatch/api/restricted_0.1.0-dev16.txt
rename to compose/runtime/runtime-dispatch/api/restricted_0.1.0-dev16.txt
diff --git a/compose/compose-dispatch/api/restricted_current.txt b/compose/runtime/runtime-dispatch/api/restricted_current.txt
similarity index 100%
rename from compose/compose-dispatch/api/restricted_current.txt
rename to compose/runtime/runtime-dispatch/api/restricted_current.txt
diff --git a/compose/compose-dispatch/build.gradle b/compose/runtime/runtime-dispatch/build.gradle
similarity index 100%
rename from compose/compose-dispatch/build.gradle
rename to compose/runtime/runtime-dispatch/build.gradle
diff --git a/compose/compose-dispatch/src/androidAndroidTest/AndroidManifest.xml b/compose/runtime/runtime-dispatch/src/androidAndroidTest/AndroidManifest.xml
similarity index 100%
rename from compose/compose-dispatch/src/androidAndroidTest/AndroidManifest.xml
rename to compose/runtime/runtime-dispatch/src/androidAndroidTest/AndroidManifest.xml
diff --git a/compose/compose-dispatch/src/androidAndroidTest/kotlin/androidx/compose/runtime/dispatch/AndroidUiDispatcherTest.kt b/compose/runtime/runtime-dispatch/src/androidAndroidTest/kotlin/androidx/compose/runtime/dispatch/AndroidUiDispatcherTest.kt
similarity index 100%
rename from compose/compose-dispatch/src/androidAndroidTest/kotlin/androidx/compose/runtime/dispatch/AndroidUiDispatcherTest.kt
rename to compose/runtime/runtime-dispatch/src/androidAndroidTest/kotlin/androidx/compose/runtime/dispatch/AndroidUiDispatcherTest.kt
diff --git a/compose/compose-dispatch/src/androidAndroidTest/kotlin/androidx/compose/runtime/dispatch/TestActivity.kt b/compose/runtime/runtime-dispatch/src/androidAndroidTest/kotlin/androidx/compose/runtime/dispatch/TestActivity.kt
similarity index 100%
rename from compose/compose-dispatch/src/androidAndroidTest/kotlin/androidx/compose/runtime/dispatch/TestActivity.kt
rename to compose/runtime/runtime-dispatch/src/androidAndroidTest/kotlin/androidx/compose/runtime/dispatch/TestActivity.kt
diff --git a/compose/compose-dispatch/src/androidMain/AndroidManifest.xml b/compose/runtime/runtime-dispatch/src/androidMain/AndroidManifest.xml
similarity index 100%
rename from compose/compose-dispatch/src/androidMain/AndroidManifest.xml
rename to compose/runtime/runtime-dispatch/src/androidMain/AndroidManifest.xml
diff --git a/compose/compose-dispatch/src/androidMain/kotlin/androidx/compose/runtime/dispatch/ActualAndroid.kt b/compose/runtime/runtime-dispatch/src/androidMain/kotlin/androidx/compose/runtime/dispatch/ActualAndroid.kt
similarity index 100%
rename from compose/compose-dispatch/src/androidMain/kotlin/androidx/compose/runtime/dispatch/ActualAndroid.kt
rename to compose/runtime/runtime-dispatch/src/androidMain/kotlin/androidx/compose/runtime/dispatch/ActualAndroid.kt
diff --git a/compose/compose-dispatch/src/androidMain/kotlin/androidx/compose/runtime/dispatch/AndroidUiDispatcher.kt b/compose/runtime/runtime-dispatch/src/androidMain/kotlin/androidx/compose/runtime/dispatch/AndroidUiDispatcher.kt
similarity index 100%
rename from compose/compose-dispatch/src/androidMain/kotlin/androidx/compose/runtime/dispatch/AndroidUiDispatcher.kt
rename to compose/runtime/runtime-dispatch/src/androidMain/kotlin/androidx/compose/runtime/dispatch/AndroidUiDispatcher.kt
diff --git a/compose/compose-dispatch/src/androidMain/kotlin/androidx/compose/runtime/dispatch/AndroidUiFrameClock.kt b/compose/runtime/runtime-dispatch/src/androidMain/kotlin/androidx/compose/runtime/dispatch/AndroidUiFrameClock.kt
similarity index 100%
rename from compose/compose-dispatch/src/androidMain/kotlin/androidx/compose/runtime/dispatch/AndroidUiFrameClock.kt
rename to compose/runtime/runtime-dispatch/src/androidMain/kotlin/androidx/compose/runtime/dispatch/AndroidUiFrameClock.kt
diff --git a/compose/compose-dispatch/src/commonMain/kotlin/androidx/compose/runtime/dispatch/BroadcastFrameClock.kt b/compose/runtime/runtime-dispatch/src/commonMain/kotlin/androidx/compose/runtime/dispatch/BroadcastFrameClock.kt
similarity index 100%
rename from compose/compose-dispatch/src/commonMain/kotlin/androidx/compose/runtime/dispatch/BroadcastFrameClock.kt
rename to compose/runtime/runtime-dispatch/src/commonMain/kotlin/androidx/compose/runtime/dispatch/BroadcastFrameClock.kt
diff --git a/compose/compose-dispatch/src/commonMain/kotlin/androidx/compose/runtime/dispatch/Expect.kt b/compose/runtime/runtime-dispatch/src/commonMain/kotlin/androidx/compose/runtime/dispatch/Expect.kt
similarity index 100%
rename from compose/compose-dispatch/src/commonMain/kotlin/androidx/compose/runtime/dispatch/Expect.kt
rename to compose/runtime/runtime-dispatch/src/commonMain/kotlin/androidx/compose/runtime/dispatch/Expect.kt
diff --git a/compose/compose-dispatch/src/commonMain/kotlin/androidx/compose/runtime/dispatch/MonotonicFrameClock.kt b/compose/runtime/runtime-dispatch/src/commonMain/kotlin/androidx/compose/runtime/dispatch/MonotonicFrameClock.kt
similarity index 100%
rename from compose/compose-dispatch/src/commonMain/kotlin/androidx/compose/runtime/dispatch/MonotonicFrameClock.kt
rename to compose/runtime/runtime-dispatch/src/commonMain/kotlin/androidx/compose/runtime/dispatch/MonotonicFrameClock.kt
diff --git a/compose/compose-dispatch/src/desktopMain/kotlin/androidx/compose/runtime/dispatch/ActualDesktop.kt b/compose/runtime/runtime-dispatch/src/desktopMain/kotlin/androidx/compose/runtime/dispatch/ActualDesktop.kt
similarity index 100%
rename from compose/compose-dispatch/src/desktopMain/kotlin/androidx/compose/runtime/dispatch/ActualDesktop.kt
rename to compose/runtime/runtime-dispatch/src/desktopMain/kotlin/androidx/compose/runtime/dispatch/ActualDesktop.kt
diff --git a/compose/compose-dispatch/src/desktopMain/kotlin/androidx/compose/runtime/dispatch/DesktopUiDispatcher.kt b/compose/runtime/runtime-dispatch/src/desktopMain/kotlin/androidx/compose/runtime/dispatch/DesktopUiDispatcher.kt
similarity index 100%
rename from compose/compose-dispatch/src/desktopMain/kotlin/androidx/compose/runtime/dispatch/DesktopUiDispatcher.kt
rename to compose/runtime/runtime-dispatch/src/desktopMain/kotlin/androidx/compose/runtime/dispatch/DesktopUiDispatcher.kt
diff --git a/ui/ui-livedata/OWNERS b/compose/runtime/runtime-livedata/OWNERS
similarity index 100%
rename from ui/ui-livedata/OWNERS
rename to compose/runtime/runtime-livedata/OWNERS
diff --git a/ui/ui-livedata/api/0.1.0-dev09.txt b/compose/runtime/runtime-livedata/api/0.1.0-dev09.txt
similarity index 100%
rename from ui/ui-livedata/api/0.1.0-dev09.txt
rename to compose/runtime/runtime-livedata/api/0.1.0-dev09.txt
diff --git a/ui/ui-livedata/api/0.1.0-dev10.txt b/compose/runtime/runtime-livedata/api/0.1.0-dev10.txt
similarity index 100%
rename from ui/ui-livedata/api/0.1.0-dev10.txt
rename to compose/runtime/runtime-livedata/api/0.1.0-dev10.txt
diff --git a/ui/ui-livedata/api/0.1.0-dev11.txt b/compose/runtime/runtime-livedata/api/0.1.0-dev11.txt
similarity index 100%
rename from ui/ui-livedata/api/0.1.0-dev11.txt
rename to compose/runtime/runtime-livedata/api/0.1.0-dev11.txt
diff --git a/ui/ui-livedata/api/0.1.0-dev12.txt b/compose/runtime/runtime-livedata/api/0.1.0-dev12.txt
similarity index 100%
rename from ui/ui-livedata/api/0.1.0-dev12.txt
rename to compose/runtime/runtime-livedata/api/0.1.0-dev12.txt
diff --git a/ui/ui-livedata/api/0.1.0-dev14.txt b/compose/runtime/runtime-livedata/api/0.1.0-dev14.txt
similarity index 100%
rename from ui/ui-livedata/api/0.1.0-dev14.txt
rename to compose/runtime/runtime-livedata/api/0.1.0-dev14.txt
diff --git a/ui/ui-livedata/api/0.1.0-dev15.txt b/compose/runtime/runtime-livedata/api/0.1.0-dev15.txt
similarity index 100%
rename from ui/ui-livedata/api/0.1.0-dev15.txt
rename to compose/runtime/runtime-livedata/api/0.1.0-dev15.txt
diff --git a/ui/ui-livedata/api/0.1.0-dev16.txt b/compose/runtime/runtime-livedata/api/0.1.0-dev16.txt
similarity index 100%
rename from ui/ui-livedata/api/0.1.0-dev16.txt
rename to compose/runtime/runtime-livedata/api/0.1.0-dev16.txt
diff --git a/ui/ui-livedata/api/current.txt b/compose/runtime/runtime-livedata/api/current.txt
similarity index 100%
rename from ui/ui-livedata/api/current.txt
rename to compose/runtime/runtime-livedata/api/current.txt
diff --git a/ui/ui-livedata/api/public_plus_experimental_0.1.0-dev09.txt b/compose/runtime/runtime-livedata/api/public_plus_experimental_0.1.0-dev09.txt
similarity index 100%
rename from ui/ui-livedata/api/public_plus_experimental_0.1.0-dev09.txt
rename to compose/runtime/runtime-livedata/api/public_plus_experimental_0.1.0-dev09.txt
diff --git a/ui/ui-livedata/api/public_plus_experimental_0.1.0-dev10.txt b/compose/runtime/runtime-livedata/api/public_plus_experimental_0.1.0-dev10.txt
similarity index 100%
rename from ui/ui-livedata/api/public_plus_experimental_0.1.0-dev10.txt
rename to compose/runtime/runtime-livedata/api/public_plus_experimental_0.1.0-dev10.txt
diff --git a/ui/ui-livedata/api/public_plus_experimental_0.1.0-dev11.txt b/compose/runtime/runtime-livedata/api/public_plus_experimental_0.1.0-dev11.txt
similarity index 100%
rename from ui/ui-livedata/api/public_plus_experimental_0.1.0-dev11.txt
rename to compose/runtime/runtime-livedata/api/public_plus_experimental_0.1.0-dev11.txt
diff --git a/ui/ui-livedata/api/public_plus_experimental_0.1.0-dev12.txt b/compose/runtime/runtime-livedata/api/public_plus_experimental_0.1.0-dev12.txt
similarity index 100%
rename from ui/ui-livedata/api/public_plus_experimental_0.1.0-dev12.txt
rename to compose/runtime/runtime-livedata/api/public_plus_experimental_0.1.0-dev12.txt
diff --git a/ui/ui-livedata/api/public_plus_experimental_0.1.0-dev14.txt b/compose/runtime/runtime-livedata/api/public_plus_experimental_0.1.0-dev14.txt
similarity index 100%
rename from ui/ui-livedata/api/public_plus_experimental_0.1.0-dev14.txt
rename to compose/runtime/runtime-livedata/api/public_plus_experimental_0.1.0-dev14.txt
diff --git a/ui/ui-livedata/api/public_plus_experimental_0.1.0-dev15.txt b/compose/runtime/runtime-livedata/api/public_plus_experimental_0.1.0-dev15.txt
similarity index 100%
rename from ui/ui-livedata/api/public_plus_experimental_0.1.0-dev15.txt
rename to compose/runtime/runtime-livedata/api/public_plus_experimental_0.1.0-dev15.txt
diff --git a/ui/ui-livedata/api/public_plus_experimental_0.1.0-dev16.txt b/compose/runtime/runtime-livedata/api/public_plus_experimental_0.1.0-dev16.txt
similarity index 100%
rename from ui/ui-livedata/api/public_plus_experimental_0.1.0-dev16.txt
rename to compose/runtime/runtime-livedata/api/public_plus_experimental_0.1.0-dev16.txt
diff --git a/ui/ui-livedata/api/public_plus_experimental_current.txt b/compose/runtime/runtime-livedata/api/public_plus_experimental_current.txt
similarity index 100%
rename from ui/ui-livedata/api/public_plus_experimental_current.txt
rename to compose/runtime/runtime-livedata/api/public_plus_experimental_current.txt
diff --git a/ui/ui-livedata/api/res-0.1.0-dev09.txt b/compose/runtime/runtime-livedata/api/res-0.1.0-dev09.txt
similarity index 100%
rename from ui/ui-livedata/api/res-0.1.0-dev09.txt
rename to compose/runtime/runtime-livedata/api/res-0.1.0-dev09.txt
diff --git a/ui/ui-livedata/api/res-0.1.0-dev10.txt b/compose/runtime/runtime-livedata/api/res-0.1.0-dev10.txt
similarity index 100%
rename from ui/ui-livedata/api/res-0.1.0-dev10.txt
rename to compose/runtime/runtime-livedata/api/res-0.1.0-dev10.txt
diff --git a/ui/ui-livedata/api/res-0.1.0-dev11.txt b/compose/runtime/runtime-livedata/api/res-0.1.0-dev11.txt
similarity index 100%
rename from ui/ui-livedata/api/res-0.1.0-dev11.txt
rename to compose/runtime/runtime-livedata/api/res-0.1.0-dev11.txt
diff --git a/ui/ui-livedata/api/res-0.1.0-dev12.txt b/compose/runtime/runtime-livedata/api/res-0.1.0-dev12.txt
similarity index 100%
rename from ui/ui-livedata/api/res-0.1.0-dev12.txt
rename to compose/runtime/runtime-livedata/api/res-0.1.0-dev12.txt
diff --git a/ui/ui-livedata/api/res-0.1.0-dev14.txt b/compose/runtime/runtime-livedata/api/res-0.1.0-dev14.txt
similarity index 100%
rename from ui/ui-livedata/api/res-0.1.0-dev14.txt
rename to compose/runtime/runtime-livedata/api/res-0.1.0-dev14.txt
diff --git a/ui/ui-livedata/api/res-0.1.0-dev15.txt b/compose/runtime/runtime-livedata/api/res-0.1.0-dev15.txt
similarity index 100%
rename from ui/ui-livedata/api/res-0.1.0-dev15.txt
rename to compose/runtime/runtime-livedata/api/res-0.1.0-dev15.txt
diff --git a/ui/ui-livedata/api/res-0.1.0-dev16.txt b/compose/runtime/runtime-livedata/api/res-0.1.0-dev16.txt
similarity index 100%
rename from ui/ui-livedata/api/res-0.1.0-dev16.txt
rename to compose/runtime/runtime-livedata/api/res-0.1.0-dev16.txt
diff --git a/ui/ui-livedata/api/res-current.txt b/compose/runtime/runtime-livedata/api/res-current.txt
similarity index 100%
rename from ui/ui-livedata/api/res-current.txt
rename to compose/runtime/runtime-livedata/api/res-current.txt
diff --git a/ui/ui-livedata/api/restricted_0.1.0-dev09.txt b/compose/runtime/runtime-livedata/api/restricted_0.1.0-dev09.txt
similarity index 100%
rename from ui/ui-livedata/api/restricted_0.1.0-dev09.txt
rename to compose/runtime/runtime-livedata/api/restricted_0.1.0-dev09.txt
diff --git a/ui/ui-livedata/api/restricted_0.1.0-dev10.txt b/compose/runtime/runtime-livedata/api/restricted_0.1.0-dev10.txt
similarity index 100%
rename from ui/ui-livedata/api/restricted_0.1.0-dev10.txt
rename to compose/runtime/runtime-livedata/api/restricted_0.1.0-dev10.txt
diff --git a/ui/ui-livedata/api/restricted_0.1.0-dev11.txt b/compose/runtime/runtime-livedata/api/restricted_0.1.0-dev11.txt
similarity index 100%
rename from ui/ui-livedata/api/restricted_0.1.0-dev11.txt
rename to compose/runtime/runtime-livedata/api/restricted_0.1.0-dev11.txt
diff --git a/ui/ui-livedata/api/restricted_0.1.0-dev12.txt b/compose/runtime/runtime-livedata/api/restricted_0.1.0-dev12.txt
similarity index 100%
rename from ui/ui-livedata/api/restricted_0.1.0-dev12.txt
rename to compose/runtime/runtime-livedata/api/restricted_0.1.0-dev12.txt
diff --git a/ui/ui-livedata/api/restricted_0.1.0-dev14.txt b/compose/runtime/runtime-livedata/api/restricted_0.1.0-dev14.txt
similarity index 100%
rename from ui/ui-livedata/api/restricted_0.1.0-dev14.txt
rename to compose/runtime/runtime-livedata/api/restricted_0.1.0-dev14.txt
diff --git a/ui/ui-livedata/api/restricted_0.1.0-dev15.txt b/compose/runtime/runtime-livedata/api/restricted_0.1.0-dev15.txt
similarity index 100%
rename from ui/ui-livedata/api/restricted_0.1.0-dev15.txt
rename to compose/runtime/runtime-livedata/api/restricted_0.1.0-dev15.txt
diff --git a/ui/ui-livedata/api/restricted_0.1.0-dev16.txt b/compose/runtime/runtime-livedata/api/restricted_0.1.0-dev16.txt
similarity index 100%
rename from ui/ui-livedata/api/restricted_0.1.0-dev16.txt
rename to compose/runtime/runtime-livedata/api/restricted_0.1.0-dev16.txt
diff --git a/ui/ui-livedata/api/restricted_current.txt b/compose/runtime/runtime-livedata/api/restricted_current.txt
similarity index 100%
rename from ui/ui-livedata/api/restricted_current.txt
rename to compose/runtime/runtime-livedata/api/restricted_current.txt
diff --git a/ui/ui-livedata/build.gradle b/compose/runtime/runtime-livedata/build.gradle
similarity index 100%
rename from ui/ui-livedata/build.gradle
rename to compose/runtime/runtime-livedata/build.gradle
diff --git a/ui/ui-livedata/samples/build.gradle b/compose/runtime/runtime-livedata/samples/build.gradle
similarity index 100%
rename from ui/ui-livedata/samples/build.gradle
rename to compose/runtime/runtime-livedata/samples/build.gradle
diff --git a/ui/ui-livedata/samples/src/main/AndroidManifest.xml b/compose/runtime/runtime-livedata/samples/src/main/AndroidManifest.xml
similarity index 100%
rename from ui/ui-livedata/samples/src/main/AndroidManifest.xml
rename to compose/runtime/runtime-livedata/samples/src/main/AndroidManifest.xml
diff --git a/ui/ui-livedata/samples/src/main/java/androidx/compose/runtime/livedata/samples/Samples.kt b/compose/runtime/runtime-livedata/samples/src/main/java/androidx/compose/runtime/livedata/samples/Samples.kt
similarity index 99%
rename from ui/ui-livedata/samples/src/main/java/androidx/compose/runtime/livedata/samples/Samples.kt
rename to compose/runtime/runtime-livedata/samples/src/main/java/androidx/compose/runtime/livedata/samples/Samples.kt
index 46fdcee..a2159f7 100644
--- a/ui/ui-livedata/samples/src/main/java/androidx/compose/runtime/livedata/samples/Samples.kt
+++ b/compose/runtime/runtime-livedata/samples/src/main/java/androidx/compose/runtime/livedata/samples/Samples.kt
@@ -17,11 +17,11 @@
 package androidx.compose.runtime.livedata.samples
 
 import androidx.annotation.Sampled
+import androidx.compose.foundation.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
-import androidx.lifecycle.LiveData
-import androidx.compose.foundation.Text
 import androidx.compose.runtime.livedata.observeAsState
+import androidx.lifecycle.LiveData
 
 @Sampled
 @Composable
diff --git a/ui/ui-livedata/src/androidTest/AndroidManifest.xml b/compose/runtime/runtime-livedata/src/androidTest/AndroidManifest.xml
similarity index 100%
rename from ui/ui-livedata/src/androidTest/AndroidManifest.xml
rename to compose/runtime/runtime-livedata/src/androidTest/AndroidManifest.xml
diff --git a/ui/ui-livedata/src/androidTest/java/androidx/compose/runtime/livedata/LiveDataAdapterTest.kt b/compose/runtime/runtime-livedata/src/androidTest/java/androidx/compose/runtime/livedata/LiveDataAdapterTest.kt
similarity index 100%
rename from ui/ui-livedata/src/androidTest/java/androidx/compose/runtime/livedata/LiveDataAdapterTest.kt
rename to compose/runtime/runtime-livedata/src/androidTest/java/androidx/compose/runtime/livedata/LiveDataAdapterTest.kt
diff --git a/ui/ui-livedata/src/main/AndroidManifest.xml b/compose/runtime/runtime-livedata/src/main/AndroidManifest.xml
similarity index 100%
rename from ui/ui-livedata/src/main/AndroidManifest.xml
rename to compose/runtime/runtime-livedata/src/main/AndroidManifest.xml
diff --git a/ui/ui-livedata/src/main/java/androidx/compose/runtime/livedata/LiveDataAdapter.kt b/compose/runtime/runtime-livedata/src/main/java/androidx/compose/runtime/livedata/LiveDataAdapter.kt
similarity index 100%
rename from ui/ui-livedata/src/main/java/androidx/compose/runtime/livedata/LiveDataAdapter.kt
rename to compose/runtime/runtime-livedata/src/main/java/androidx/compose/runtime/livedata/LiveDataAdapter.kt
diff --git a/ui/ui-rxjava2/OWNERS b/compose/runtime/runtime-rxjava2/OWNERS
similarity index 100%
rename from ui/ui-rxjava2/OWNERS
rename to compose/runtime/runtime-rxjava2/OWNERS
diff --git a/ui/ui-rxjava2/api/0.1.0-dev09.txt b/compose/runtime/runtime-rxjava2/api/0.1.0-dev09.txt
similarity index 100%
rename from ui/ui-rxjava2/api/0.1.0-dev09.txt
rename to compose/runtime/runtime-rxjava2/api/0.1.0-dev09.txt
diff --git a/ui/ui-rxjava2/api/0.1.0-dev10.txt b/compose/runtime/runtime-rxjava2/api/0.1.0-dev10.txt
similarity index 100%
rename from ui/ui-rxjava2/api/0.1.0-dev10.txt
rename to compose/runtime/runtime-rxjava2/api/0.1.0-dev10.txt
diff --git a/ui/ui-rxjava2/api/0.1.0-dev11.txt b/compose/runtime/runtime-rxjava2/api/0.1.0-dev11.txt
similarity index 100%
rename from ui/ui-rxjava2/api/0.1.0-dev11.txt
rename to compose/runtime/runtime-rxjava2/api/0.1.0-dev11.txt
diff --git a/ui/ui-rxjava2/api/0.1.0-dev12.txt b/compose/runtime/runtime-rxjava2/api/0.1.0-dev12.txt
similarity index 100%
rename from ui/ui-rxjava2/api/0.1.0-dev12.txt
rename to compose/runtime/runtime-rxjava2/api/0.1.0-dev12.txt
diff --git a/ui/ui-rxjava2/api/0.1.0-dev14.txt b/compose/runtime/runtime-rxjava2/api/0.1.0-dev14.txt
similarity index 100%
rename from ui/ui-rxjava2/api/0.1.0-dev14.txt
rename to compose/runtime/runtime-rxjava2/api/0.1.0-dev14.txt
diff --git a/ui/ui-rxjava2/api/0.1.0-dev15.txt b/compose/runtime/runtime-rxjava2/api/0.1.0-dev15.txt
similarity index 100%
rename from ui/ui-rxjava2/api/0.1.0-dev15.txt
rename to compose/runtime/runtime-rxjava2/api/0.1.0-dev15.txt
diff --git a/ui/ui-rxjava2/api/0.1.0-dev16.txt b/compose/runtime/runtime-rxjava2/api/0.1.0-dev16.txt
similarity index 100%
rename from ui/ui-rxjava2/api/0.1.0-dev16.txt
rename to compose/runtime/runtime-rxjava2/api/0.1.0-dev16.txt
diff --git a/ui/ui-rxjava2/api/current.txt b/compose/runtime/runtime-rxjava2/api/current.txt
similarity index 100%
rename from ui/ui-rxjava2/api/current.txt
rename to compose/runtime/runtime-rxjava2/api/current.txt
diff --git a/ui/ui-rxjava2/api/public_plus_experimental_0.1.0-dev09.txt b/compose/runtime/runtime-rxjava2/api/public_plus_experimental_0.1.0-dev09.txt
similarity index 100%
rename from ui/ui-rxjava2/api/public_plus_experimental_0.1.0-dev09.txt
rename to compose/runtime/runtime-rxjava2/api/public_plus_experimental_0.1.0-dev09.txt
diff --git a/ui/ui-rxjava2/api/public_plus_experimental_0.1.0-dev10.txt b/compose/runtime/runtime-rxjava2/api/public_plus_experimental_0.1.0-dev10.txt
similarity index 100%
rename from ui/ui-rxjava2/api/public_plus_experimental_0.1.0-dev10.txt
rename to compose/runtime/runtime-rxjava2/api/public_plus_experimental_0.1.0-dev10.txt
diff --git a/ui/ui-rxjava2/api/public_plus_experimental_0.1.0-dev11.txt b/compose/runtime/runtime-rxjava2/api/public_plus_experimental_0.1.0-dev11.txt
similarity index 100%
rename from ui/ui-rxjava2/api/public_plus_experimental_0.1.0-dev11.txt
rename to compose/runtime/runtime-rxjava2/api/public_plus_experimental_0.1.0-dev11.txt
diff --git a/ui/ui-rxjava2/api/public_plus_experimental_0.1.0-dev12.txt b/compose/runtime/runtime-rxjava2/api/public_plus_experimental_0.1.0-dev12.txt
similarity index 100%
rename from ui/ui-rxjava2/api/public_plus_experimental_0.1.0-dev12.txt
rename to compose/runtime/runtime-rxjava2/api/public_plus_experimental_0.1.0-dev12.txt
diff --git a/ui/ui-rxjava2/api/public_plus_experimental_0.1.0-dev14.txt b/compose/runtime/runtime-rxjava2/api/public_plus_experimental_0.1.0-dev14.txt
similarity index 100%
rename from ui/ui-rxjava2/api/public_plus_experimental_0.1.0-dev14.txt
rename to compose/runtime/runtime-rxjava2/api/public_plus_experimental_0.1.0-dev14.txt
diff --git a/ui/ui-rxjava2/api/public_plus_experimental_0.1.0-dev15.txt b/compose/runtime/runtime-rxjava2/api/public_plus_experimental_0.1.0-dev15.txt
similarity index 100%
rename from ui/ui-rxjava2/api/public_plus_experimental_0.1.0-dev15.txt
rename to compose/runtime/runtime-rxjava2/api/public_plus_experimental_0.1.0-dev15.txt
diff --git a/ui/ui-rxjava2/api/public_plus_experimental_0.1.0-dev16.txt b/compose/runtime/runtime-rxjava2/api/public_plus_experimental_0.1.0-dev16.txt
similarity index 100%
rename from ui/ui-rxjava2/api/public_plus_experimental_0.1.0-dev16.txt
rename to compose/runtime/runtime-rxjava2/api/public_plus_experimental_0.1.0-dev16.txt
diff --git a/ui/ui-rxjava2/api/public_plus_experimental_current.txt b/compose/runtime/runtime-rxjava2/api/public_plus_experimental_current.txt
similarity index 100%
rename from ui/ui-rxjava2/api/public_plus_experimental_current.txt
rename to compose/runtime/runtime-rxjava2/api/public_plus_experimental_current.txt
diff --git a/ui/ui-rxjava2/api/res-0.1.0-dev09.txt b/compose/runtime/runtime-rxjava2/api/res-0.1.0-dev09.txt
similarity index 100%
rename from ui/ui-rxjava2/api/res-0.1.0-dev09.txt
rename to compose/runtime/runtime-rxjava2/api/res-0.1.0-dev09.txt
diff --git a/ui/ui-rxjava2/api/res-0.1.0-dev10.txt b/compose/runtime/runtime-rxjava2/api/res-0.1.0-dev10.txt
similarity index 100%
rename from ui/ui-rxjava2/api/res-0.1.0-dev10.txt
rename to compose/runtime/runtime-rxjava2/api/res-0.1.0-dev10.txt
diff --git a/ui/ui-rxjava2/api/res-0.1.0-dev11.txt b/compose/runtime/runtime-rxjava2/api/res-0.1.0-dev11.txt
similarity index 100%
rename from ui/ui-rxjava2/api/res-0.1.0-dev11.txt
rename to compose/runtime/runtime-rxjava2/api/res-0.1.0-dev11.txt
diff --git a/ui/ui-rxjava2/api/res-0.1.0-dev12.txt b/compose/runtime/runtime-rxjava2/api/res-0.1.0-dev12.txt
similarity index 100%
rename from ui/ui-rxjava2/api/res-0.1.0-dev12.txt
rename to compose/runtime/runtime-rxjava2/api/res-0.1.0-dev12.txt
diff --git a/ui/ui-rxjava2/api/res-0.1.0-dev14.txt b/compose/runtime/runtime-rxjava2/api/res-0.1.0-dev14.txt
similarity index 100%
rename from ui/ui-rxjava2/api/res-0.1.0-dev14.txt
rename to compose/runtime/runtime-rxjava2/api/res-0.1.0-dev14.txt
diff --git a/ui/ui-rxjava2/api/res-0.1.0-dev15.txt b/compose/runtime/runtime-rxjava2/api/res-0.1.0-dev15.txt
similarity index 100%
rename from ui/ui-rxjava2/api/res-0.1.0-dev15.txt
rename to compose/runtime/runtime-rxjava2/api/res-0.1.0-dev15.txt
diff --git a/ui/ui-rxjava2/api/res-0.1.0-dev16.txt b/compose/runtime/runtime-rxjava2/api/res-0.1.0-dev16.txt
similarity index 100%
rename from ui/ui-rxjava2/api/res-0.1.0-dev16.txt
rename to compose/runtime/runtime-rxjava2/api/res-0.1.0-dev16.txt
diff --git a/ui/ui-rxjava2/api/res-current.txt b/compose/runtime/runtime-rxjava2/api/res-current.txt
similarity index 100%
rename from ui/ui-rxjava2/api/res-current.txt
rename to compose/runtime/runtime-rxjava2/api/res-current.txt
diff --git a/ui/ui-rxjava2/api/restricted_0.1.0-dev09.txt b/compose/runtime/runtime-rxjava2/api/restricted_0.1.0-dev09.txt
similarity index 100%
rename from ui/ui-rxjava2/api/restricted_0.1.0-dev09.txt
rename to compose/runtime/runtime-rxjava2/api/restricted_0.1.0-dev09.txt
diff --git a/ui/ui-rxjava2/api/restricted_0.1.0-dev10.txt b/compose/runtime/runtime-rxjava2/api/restricted_0.1.0-dev10.txt
similarity index 100%
rename from ui/ui-rxjava2/api/restricted_0.1.0-dev10.txt
rename to compose/runtime/runtime-rxjava2/api/restricted_0.1.0-dev10.txt
diff --git a/ui/ui-rxjava2/api/restricted_0.1.0-dev11.txt b/compose/runtime/runtime-rxjava2/api/restricted_0.1.0-dev11.txt
similarity index 100%
rename from ui/ui-rxjava2/api/restricted_0.1.0-dev11.txt
rename to compose/runtime/runtime-rxjava2/api/restricted_0.1.0-dev11.txt
diff --git a/ui/ui-rxjava2/api/restricted_0.1.0-dev12.txt b/compose/runtime/runtime-rxjava2/api/restricted_0.1.0-dev12.txt
similarity index 100%
rename from ui/ui-rxjava2/api/restricted_0.1.0-dev12.txt
rename to compose/runtime/runtime-rxjava2/api/restricted_0.1.0-dev12.txt
diff --git a/ui/ui-rxjava2/api/restricted_0.1.0-dev14.txt b/compose/runtime/runtime-rxjava2/api/restricted_0.1.0-dev14.txt
similarity index 100%
rename from ui/ui-rxjava2/api/restricted_0.1.0-dev14.txt
rename to compose/runtime/runtime-rxjava2/api/restricted_0.1.0-dev14.txt
diff --git a/ui/ui-rxjava2/api/restricted_0.1.0-dev15.txt b/compose/runtime/runtime-rxjava2/api/restricted_0.1.0-dev15.txt
similarity index 100%
rename from ui/ui-rxjava2/api/restricted_0.1.0-dev15.txt
rename to compose/runtime/runtime-rxjava2/api/restricted_0.1.0-dev15.txt
diff --git a/ui/ui-rxjava2/api/restricted_0.1.0-dev16.txt b/compose/runtime/runtime-rxjava2/api/restricted_0.1.0-dev16.txt
similarity index 100%
rename from ui/ui-rxjava2/api/restricted_0.1.0-dev16.txt
rename to compose/runtime/runtime-rxjava2/api/restricted_0.1.0-dev16.txt
diff --git a/ui/ui-rxjava2/api/restricted_current.txt b/compose/runtime/runtime-rxjava2/api/restricted_current.txt
similarity index 100%
rename from ui/ui-rxjava2/api/restricted_current.txt
rename to compose/runtime/runtime-rxjava2/api/restricted_current.txt
diff --git a/ui/ui-rxjava2/build.gradle b/compose/runtime/runtime-rxjava2/build.gradle
similarity index 100%
rename from ui/ui-rxjava2/build.gradle
rename to compose/runtime/runtime-rxjava2/build.gradle
diff --git a/ui/ui-rxjava2/samples/build.gradle b/compose/runtime/runtime-rxjava2/samples/build.gradle
similarity index 100%
rename from ui/ui-rxjava2/samples/build.gradle
rename to compose/runtime/runtime-rxjava2/samples/build.gradle
diff --git a/ui/ui-rxjava2/samples/src/main/AndroidManifest.xml b/compose/runtime/runtime-rxjava2/samples/src/main/AndroidManifest.xml
similarity index 100%
rename from ui/ui-rxjava2/samples/src/main/AndroidManifest.xml
rename to compose/runtime/runtime-rxjava2/samples/src/main/AndroidManifest.xml
diff --git a/ui/ui-rxjava2/samples/src/main/java/androidx/compose/runtime/rxjava2/samples/Samples.kt b/compose/runtime/runtime-rxjava2/samples/src/main/java/androidx/compose/runtime/rxjava2/samples/Samples.kt
similarity index 99%
rename from ui/ui-rxjava2/samples/src/main/java/androidx/compose/runtime/rxjava2/samples/Samples.kt
rename to compose/runtime/runtime-rxjava2/samples/src/main/java/androidx/compose/runtime/rxjava2/samples/Samples.kt
index 277271c..be82303 100644
--- a/ui/ui-rxjava2/samples/src/main/java/androidx/compose/runtime/rxjava2/samples/Samples.kt
+++ b/compose/runtime/runtime-rxjava2/samples/src/main/java/androidx/compose/runtime/rxjava2/samples/Samples.kt
@@ -17,9 +17,9 @@
 package androidx.compose.runtime.rxjava2.samples
 
 import androidx.annotation.Sampled
+import androidx.compose.foundation.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
-import androidx.compose.foundation.Text
 import androidx.compose.runtime.rxjava2.subscribeAsState
 import io.reactivex.Completable
 import io.reactivex.Flowable
diff --git a/ui/ui-rxjava2/src/androidTest/AndroidManifest.xml b/compose/runtime/runtime-rxjava2/src/androidTest/AndroidManifest.xml
similarity index 100%
rename from ui/ui-rxjava2/src/androidTest/AndroidManifest.xml
rename to compose/runtime/runtime-rxjava2/src/androidTest/AndroidManifest.xml
diff --git a/ui/ui-rxjava2/src/androidTest/java/androidx/compose/runtime/rxjava2/CompletableAdapterTest.kt b/compose/runtime/runtime-rxjava2/src/androidTest/java/androidx/compose/runtime/rxjava2/CompletableAdapterTest.kt
similarity index 100%
rename from ui/ui-rxjava2/src/androidTest/java/androidx/compose/runtime/rxjava2/CompletableAdapterTest.kt
rename to compose/runtime/runtime-rxjava2/src/androidTest/java/androidx/compose/runtime/rxjava2/CompletableAdapterTest.kt
diff --git a/ui/ui-rxjava2/src/androidTest/java/androidx/compose/runtime/rxjava2/RxJava2AdapterTest.kt b/compose/runtime/runtime-rxjava2/src/androidTest/java/androidx/compose/runtime/rxjava2/RxJava2AdapterTest.kt
similarity index 100%
rename from ui/ui-rxjava2/src/androidTest/java/androidx/compose/runtime/rxjava2/RxJava2AdapterTest.kt
rename to compose/runtime/runtime-rxjava2/src/androidTest/java/androidx/compose/runtime/rxjava2/RxJava2AdapterTest.kt
diff --git a/ui/ui-rxjava2/src/main/AndroidManifest.xml b/compose/runtime/runtime-rxjava2/src/main/AndroidManifest.xml
similarity index 100%
rename from ui/ui-rxjava2/src/main/AndroidManifest.xml
rename to compose/runtime/runtime-rxjava2/src/main/AndroidManifest.xml
diff --git a/ui/ui-rxjava2/src/main/java/androidx/compose/runtime/rxjava2/RxJava2Adapter.kt b/compose/runtime/runtime-rxjava2/src/main/java/androidx/compose/runtime/rxjava2/RxJava2Adapter.kt
similarity index 100%
rename from ui/ui-rxjava2/src/main/java/androidx/compose/runtime/rxjava2/RxJava2Adapter.kt
rename to compose/runtime/runtime-rxjava2/src/main/java/androidx/compose/runtime/rxjava2/RxJava2Adapter.kt
diff --git a/ui/ui-saved-instance-state/OWNERS b/compose/runtime/runtime-saved-instance-state/OWNERS
similarity index 100%
rename from ui/ui-saved-instance-state/OWNERS
rename to compose/runtime/runtime-saved-instance-state/OWNERS
diff --git a/ui/ui-saved-instance-state/api/0.1.0-dev07.txt b/compose/runtime/runtime-saved-instance-state/api/0.1.0-dev07.txt
similarity index 100%
rename from ui/ui-saved-instance-state/api/0.1.0-dev07.txt
rename to compose/runtime/runtime-saved-instance-state/api/0.1.0-dev07.txt
diff --git a/ui/ui-saved-instance-state/api/0.1.0-dev08.txt b/compose/runtime/runtime-saved-instance-state/api/0.1.0-dev08.txt
similarity index 100%
rename from ui/ui-saved-instance-state/api/0.1.0-dev08.txt
rename to compose/runtime/runtime-saved-instance-state/api/0.1.0-dev08.txt
diff --git a/ui/ui-saved-instance-state/api/0.1.0-dev09.txt b/compose/runtime/runtime-saved-instance-state/api/0.1.0-dev09.txt
similarity index 100%
rename from ui/ui-saved-instance-state/api/0.1.0-dev09.txt
rename to compose/runtime/runtime-saved-instance-state/api/0.1.0-dev09.txt
diff --git a/ui/ui-saved-instance-state/api/0.1.0-dev10.txt b/compose/runtime/runtime-saved-instance-state/api/0.1.0-dev10.txt
similarity index 100%
rename from ui/ui-saved-instance-state/api/0.1.0-dev10.txt
rename to compose/runtime/runtime-saved-instance-state/api/0.1.0-dev10.txt
diff --git a/ui/ui-saved-instance-state/api/0.1.0-dev11.txt b/compose/runtime/runtime-saved-instance-state/api/0.1.0-dev11.txt
similarity index 100%
rename from ui/ui-saved-instance-state/api/0.1.0-dev11.txt
rename to compose/runtime/runtime-saved-instance-state/api/0.1.0-dev11.txt
diff --git a/ui/ui-saved-instance-state/api/0.1.0-dev12.txt b/compose/runtime/runtime-saved-instance-state/api/0.1.0-dev12.txt
similarity index 100%
rename from ui/ui-saved-instance-state/api/0.1.0-dev12.txt
rename to compose/runtime/runtime-saved-instance-state/api/0.1.0-dev12.txt
diff --git a/ui/ui-saved-instance-state/api/0.1.0-dev14.txt b/compose/runtime/runtime-saved-instance-state/api/0.1.0-dev14.txt
similarity index 100%
rename from ui/ui-saved-instance-state/api/0.1.0-dev14.txt
rename to compose/runtime/runtime-saved-instance-state/api/0.1.0-dev14.txt
diff --git a/ui/ui-saved-instance-state/api/0.1.0-dev15.txt b/compose/runtime/runtime-saved-instance-state/api/0.1.0-dev15.txt
similarity index 100%
rename from ui/ui-saved-instance-state/api/0.1.0-dev15.txt
rename to compose/runtime/runtime-saved-instance-state/api/0.1.0-dev15.txt
diff --git a/ui/ui-saved-instance-state/api/0.1.0-dev16.txt b/compose/runtime/runtime-saved-instance-state/api/0.1.0-dev16.txt
similarity index 100%
rename from ui/ui-saved-instance-state/api/0.1.0-dev16.txt
rename to compose/runtime/runtime-saved-instance-state/api/0.1.0-dev16.txt
diff --git a/ui/ui-saved-instance-state/api/api_lint.ignore b/compose/runtime/runtime-saved-instance-state/api/api_lint.ignore
similarity index 100%
rename from ui/ui-saved-instance-state/api/api_lint.ignore
rename to compose/runtime/runtime-saved-instance-state/api/api_lint.ignore
diff --git a/ui/ui-saved-instance-state/api/current.txt b/compose/runtime/runtime-saved-instance-state/api/current.txt
similarity index 100%
rename from ui/ui-saved-instance-state/api/current.txt
rename to compose/runtime/runtime-saved-instance-state/api/current.txt
diff --git a/ui/ui-saved-instance-state/api/public_plus_experimental_0.1.0-dev07.txt b/compose/runtime/runtime-saved-instance-state/api/public_plus_experimental_0.1.0-dev07.txt
similarity index 100%
rename from ui/ui-saved-instance-state/api/public_plus_experimental_0.1.0-dev07.txt
rename to compose/runtime/runtime-saved-instance-state/api/public_plus_experimental_0.1.0-dev07.txt
diff --git a/ui/ui-saved-instance-state/api/public_plus_experimental_0.1.0-dev08.txt b/compose/runtime/runtime-saved-instance-state/api/public_plus_experimental_0.1.0-dev08.txt
similarity index 100%
rename from ui/ui-saved-instance-state/api/public_plus_experimental_0.1.0-dev08.txt
rename to compose/runtime/runtime-saved-instance-state/api/public_plus_experimental_0.1.0-dev08.txt
diff --git a/ui/ui-saved-instance-state/api/public_plus_experimental_0.1.0-dev09.txt b/compose/runtime/runtime-saved-instance-state/api/public_plus_experimental_0.1.0-dev09.txt
similarity index 100%
rename from ui/ui-saved-instance-state/api/public_plus_experimental_0.1.0-dev09.txt
rename to compose/runtime/runtime-saved-instance-state/api/public_plus_experimental_0.1.0-dev09.txt
diff --git a/ui/ui-saved-instance-state/api/public_plus_experimental_0.1.0-dev10.txt b/compose/runtime/runtime-saved-instance-state/api/public_plus_experimental_0.1.0-dev10.txt
similarity index 100%
rename from ui/ui-saved-instance-state/api/public_plus_experimental_0.1.0-dev10.txt
rename to compose/runtime/runtime-saved-instance-state/api/public_plus_experimental_0.1.0-dev10.txt
diff --git a/ui/ui-saved-instance-state/api/public_plus_experimental_0.1.0-dev11.txt b/compose/runtime/runtime-saved-instance-state/api/public_plus_experimental_0.1.0-dev11.txt
similarity index 100%
rename from ui/ui-saved-instance-state/api/public_plus_experimental_0.1.0-dev11.txt
rename to compose/runtime/runtime-saved-instance-state/api/public_plus_experimental_0.1.0-dev11.txt
diff --git a/ui/ui-saved-instance-state/api/public_plus_experimental_0.1.0-dev12.txt b/compose/runtime/runtime-saved-instance-state/api/public_plus_experimental_0.1.0-dev12.txt
similarity index 100%
rename from ui/ui-saved-instance-state/api/public_plus_experimental_0.1.0-dev12.txt
rename to compose/runtime/runtime-saved-instance-state/api/public_plus_experimental_0.1.0-dev12.txt
diff --git a/ui/ui-saved-instance-state/api/public_plus_experimental_0.1.0-dev14.txt b/compose/runtime/runtime-saved-instance-state/api/public_plus_experimental_0.1.0-dev14.txt
similarity index 100%
rename from ui/ui-saved-instance-state/api/public_plus_experimental_0.1.0-dev14.txt
rename to compose/runtime/runtime-saved-instance-state/api/public_plus_experimental_0.1.0-dev14.txt
diff --git a/ui/ui-saved-instance-state/api/public_plus_experimental_0.1.0-dev15.txt b/compose/runtime/runtime-saved-instance-state/api/public_plus_experimental_0.1.0-dev15.txt
similarity index 100%
rename from ui/ui-saved-instance-state/api/public_plus_experimental_0.1.0-dev15.txt
rename to compose/runtime/runtime-saved-instance-state/api/public_plus_experimental_0.1.0-dev15.txt
diff --git a/ui/ui-saved-instance-state/api/public_plus_experimental_0.1.0-dev16.txt b/compose/runtime/runtime-saved-instance-state/api/public_plus_experimental_0.1.0-dev16.txt
similarity index 100%
rename from ui/ui-saved-instance-state/api/public_plus_experimental_0.1.0-dev16.txt
rename to compose/runtime/runtime-saved-instance-state/api/public_plus_experimental_0.1.0-dev16.txt
diff --git a/ui/ui-saved-instance-state/api/public_plus_experimental_current.txt b/compose/runtime/runtime-saved-instance-state/api/public_plus_experimental_current.txt
similarity index 100%
rename from ui/ui-saved-instance-state/api/public_plus_experimental_current.txt
rename to compose/runtime/runtime-saved-instance-state/api/public_plus_experimental_current.txt
diff --git a/ui/ui-saved-instance-state/api/res-0.1.0-dev07.txt b/compose/runtime/runtime-saved-instance-state/api/res-0.1.0-dev07.txt
similarity index 100%
rename from ui/ui-saved-instance-state/api/res-0.1.0-dev07.txt
rename to compose/runtime/runtime-saved-instance-state/api/res-0.1.0-dev07.txt
diff --git a/ui/ui-saved-instance-state/api/res-0.1.0-dev08.txt b/compose/runtime/runtime-saved-instance-state/api/res-0.1.0-dev08.txt
similarity index 100%
rename from ui/ui-saved-instance-state/api/res-0.1.0-dev08.txt
rename to compose/runtime/runtime-saved-instance-state/api/res-0.1.0-dev08.txt
diff --git a/ui/ui-saved-instance-state/api/res-0.1.0-dev09.txt b/compose/runtime/runtime-saved-instance-state/api/res-0.1.0-dev09.txt
similarity index 100%
rename from ui/ui-saved-instance-state/api/res-0.1.0-dev09.txt
rename to compose/runtime/runtime-saved-instance-state/api/res-0.1.0-dev09.txt
diff --git a/ui/ui-saved-instance-state/api/res-0.1.0-dev10.txt b/compose/runtime/runtime-saved-instance-state/api/res-0.1.0-dev10.txt
similarity index 100%
rename from ui/ui-saved-instance-state/api/res-0.1.0-dev10.txt
rename to compose/runtime/runtime-saved-instance-state/api/res-0.1.0-dev10.txt
diff --git a/ui/ui-saved-instance-state/api/res-0.1.0-dev11.txt b/compose/runtime/runtime-saved-instance-state/api/res-0.1.0-dev11.txt
similarity index 100%
rename from ui/ui-saved-instance-state/api/res-0.1.0-dev11.txt
rename to compose/runtime/runtime-saved-instance-state/api/res-0.1.0-dev11.txt
diff --git a/ui/ui-saved-instance-state/api/res-0.1.0-dev12.txt b/compose/runtime/runtime-saved-instance-state/api/res-0.1.0-dev12.txt
similarity index 100%
rename from ui/ui-saved-instance-state/api/res-0.1.0-dev12.txt
rename to compose/runtime/runtime-saved-instance-state/api/res-0.1.0-dev12.txt
diff --git a/ui/ui-saved-instance-state/api/res-0.1.0-dev14.txt b/compose/runtime/runtime-saved-instance-state/api/res-0.1.0-dev14.txt
similarity index 100%
rename from ui/ui-saved-instance-state/api/res-0.1.0-dev14.txt
rename to compose/runtime/runtime-saved-instance-state/api/res-0.1.0-dev14.txt
diff --git a/ui/ui-saved-instance-state/api/res-0.1.0-dev15.txt b/compose/runtime/runtime-saved-instance-state/api/res-0.1.0-dev15.txt
similarity index 100%
rename from ui/ui-saved-instance-state/api/res-0.1.0-dev15.txt
rename to compose/runtime/runtime-saved-instance-state/api/res-0.1.0-dev15.txt
diff --git a/ui/ui-saved-instance-state/api/res-0.1.0-dev16.txt b/compose/runtime/runtime-saved-instance-state/api/res-0.1.0-dev16.txt
similarity index 100%
rename from ui/ui-saved-instance-state/api/res-0.1.0-dev16.txt
rename to compose/runtime/runtime-saved-instance-state/api/res-0.1.0-dev16.txt
diff --git a/ui/ui-saved-instance-state/api/res-current.txt b/compose/runtime/runtime-saved-instance-state/api/res-current.txt
similarity index 100%
rename from ui/ui-saved-instance-state/api/res-current.txt
rename to compose/runtime/runtime-saved-instance-state/api/res-current.txt
diff --git a/ui/ui-saved-instance-state/api/restricted_0.1.0-dev07.txt b/compose/runtime/runtime-saved-instance-state/api/restricted_0.1.0-dev07.txt
similarity index 100%
rename from ui/ui-saved-instance-state/api/restricted_0.1.0-dev07.txt
rename to compose/runtime/runtime-saved-instance-state/api/restricted_0.1.0-dev07.txt
diff --git a/ui/ui-saved-instance-state/api/restricted_0.1.0-dev08.txt b/compose/runtime/runtime-saved-instance-state/api/restricted_0.1.0-dev08.txt
similarity index 100%
rename from ui/ui-saved-instance-state/api/restricted_0.1.0-dev08.txt
rename to compose/runtime/runtime-saved-instance-state/api/restricted_0.1.0-dev08.txt
diff --git a/ui/ui-saved-instance-state/api/restricted_0.1.0-dev09.txt b/compose/runtime/runtime-saved-instance-state/api/restricted_0.1.0-dev09.txt
similarity index 100%
rename from ui/ui-saved-instance-state/api/restricted_0.1.0-dev09.txt
rename to compose/runtime/runtime-saved-instance-state/api/restricted_0.1.0-dev09.txt
diff --git a/ui/ui-saved-instance-state/api/restricted_0.1.0-dev10.txt b/compose/runtime/runtime-saved-instance-state/api/restricted_0.1.0-dev10.txt
similarity index 100%
rename from ui/ui-saved-instance-state/api/restricted_0.1.0-dev10.txt
rename to compose/runtime/runtime-saved-instance-state/api/restricted_0.1.0-dev10.txt
diff --git a/ui/ui-saved-instance-state/api/restricted_0.1.0-dev11.txt b/compose/runtime/runtime-saved-instance-state/api/restricted_0.1.0-dev11.txt
similarity index 100%
rename from ui/ui-saved-instance-state/api/restricted_0.1.0-dev11.txt
rename to compose/runtime/runtime-saved-instance-state/api/restricted_0.1.0-dev11.txt
diff --git a/ui/ui-saved-instance-state/api/restricted_0.1.0-dev12.txt b/compose/runtime/runtime-saved-instance-state/api/restricted_0.1.0-dev12.txt
similarity index 100%
rename from ui/ui-saved-instance-state/api/restricted_0.1.0-dev12.txt
rename to compose/runtime/runtime-saved-instance-state/api/restricted_0.1.0-dev12.txt
diff --git a/ui/ui-saved-instance-state/api/restricted_0.1.0-dev14.txt b/compose/runtime/runtime-saved-instance-state/api/restricted_0.1.0-dev14.txt
similarity index 100%
rename from ui/ui-saved-instance-state/api/restricted_0.1.0-dev14.txt
rename to compose/runtime/runtime-saved-instance-state/api/restricted_0.1.0-dev14.txt
diff --git a/ui/ui-saved-instance-state/api/restricted_0.1.0-dev15.txt b/compose/runtime/runtime-saved-instance-state/api/restricted_0.1.0-dev15.txt
similarity index 100%
rename from ui/ui-saved-instance-state/api/restricted_0.1.0-dev15.txt
rename to compose/runtime/runtime-saved-instance-state/api/restricted_0.1.0-dev15.txt
diff --git a/ui/ui-saved-instance-state/api/restricted_0.1.0-dev16.txt b/compose/runtime/runtime-saved-instance-state/api/restricted_0.1.0-dev16.txt
similarity index 100%
rename from ui/ui-saved-instance-state/api/restricted_0.1.0-dev16.txt
rename to compose/runtime/runtime-saved-instance-state/api/restricted_0.1.0-dev16.txt
diff --git a/ui/ui-saved-instance-state/api/restricted_current.txt b/compose/runtime/runtime-saved-instance-state/api/restricted_current.txt
similarity index 100%
rename from ui/ui-saved-instance-state/api/restricted_current.txt
rename to compose/runtime/runtime-saved-instance-state/api/restricted_current.txt
diff --git a/ui/ui-saved-instance-state/build.gradle b/compose/runtime/runtime-saved-instance-state/build.gradle
similarity index 100%
rename from ui/ui-saved-instance-state/build.gradle
rename to compose/runtime/runtime-saved-instance-state/build.gradle
diff --git a/ui/ui-saved-instance-state/samples/build.gradle b/compose/runtime/runtime-saved-instance-state/samples/build.gradle
similarity index 100%
rename from ui/ui-saved-instance-state/samples/build.gradle
rename to compose/runtime/runtime-saved-instance-state/samples/build.gradle
diff --git a/ui/ui-saved-instance-state/samples/src/main/AndroidManifest.xml b/compose/runtime/runtime-saved-instance-state/samples/src/main/AndroidManifest.xml
similarity index 100%
rename from ui/ui-saved-instance-state/samples/src/main/AndroidManifest.xml
rename to compose/runtime/runtime-saved-instance-state/samples/src/main/AndroidManifest.xml
diff --git a/ui/ui-saved-instance-state/samples/src/main/java/androidx/compose/runtime/savedinstancestate/samples/Samples.kt b/compose/runtime/runtime-saved-instance-state/samples/src/main/java/androidx/compose/runtime/savedinstancestate/samples/Samples.kt
similarity index 99%
rename from ui/ui-saved-instance-state/samples/src/main/java/androidx/compose/runtime/savedinstancestate/samples/Samples.kt
rename to compose/runtime/runtime-saved-instance-state/samples/src/main/java/androidx/compose/runtime/savedinstancestate/samples/Samples.kt
index 3894bef..3ee23a6 100644
--- a/ui/ui-saved-instance-state/samples/src/main/java/androidx/compose/runtime/savedinstancestate/samples/Samples.kt
+++ b/compose/runtime/runtime-saved-instance-state/samples/src/main/java/androidx/compose/runtime/savedinstancestate/samples/Samples.kt
@@ -21,12 +21,12 @@
 import androidx.annotation.Sampled
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
-import androidx.compose.runtime.setValue
 import androidx.compose.runtime.savedinstancestate.Saver
 import androidx.compose.runtime.savedinstancestate.listSaver
 import androidx.compose.runtime.savedinstancestate.mapSaver
 import androidx.compose.runtime.savedinstancestate.rememberSavedInstanceState
 import androidx.compose.runtime.savedinstancestate.savedInstanceState
+import androidx.compose.runtime.setValue
 
 @Sampled
 @Composable
diff --git a/ui/ui-saved-instance-state/src/androidAndroidTest/AndroidManifest.xml b/compose/runtime/runtime-saved-instance-state/src/androidAndroidTest/AndroidManifest.xml
similarity index 100%
rename from ui/ui-saved-instance-state/src/androidAndroidTest/AndroidManifest.xml
rename to compose/runtime/runtime-saved-instance-state/src/androidAndroidTest/AndroidManifest.xml
diff --git a/ui/ui-saved-instance-state/src/androidAndroidTest/kotlin/androidx/compose/runtime/savedinstancestate/ActivityRecreationTest.kt b/compose/runtime/runtime-saved-instance-state/src/androidAndroidTest/kotlin/androidx/compose/runtime/savedinstancestate/ActivityRecreationTest.kt
similarity index 99%
rename from ui/ui-saved-instance-state/src/androidAndroidTest/kotlin/androidx/compose/runtime/savedinstancestate/ActivityRecreationTest.kt
rename to compose/runtime/runtime-saved-instance-state/src/androidAndroidTest/kotlin/androidx/compose/runtime/savedinstancestate/ActivityRecreationTest.kt
index 53c8e56..6e03fe3 100644
--- a/ui/ui-saved-instance-state/src/androidAndroidTest/kotlin/androidx/compose/runtime/savedinstancestate/ActivityRecreationTest.kt
+++ b/compose/runtime/runtime-saved-instance-state/src/androidAndroidTest/kotlin/androidx/compose/runtime/savedinstancestate/ActivityRecreationTest.kt
@@ -24,13 +24,13 @@
 import androidx.activity.ComponentActivity
 import androidx.compose.runtime.Recomposer
 import androidx.compose.runtime.remember
+import androidx.compose.runtime.savedinstancestate.test.R
+import androidx.compose.ui.platform.setContent
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.FragmentActivity
 import androidx.lifecycle.Lifecycle
 import androidx.test.core.app.ActivityScenario
 import androidx.test.filters.MediumTest
-import androidx.compose.ui.platform.setContent
-import androidx.compose.runtime.savedinstancestate.test.R
-import androidx.fragment.app.Fragment
-import androidx.fragment.app.FragmentActivity
 import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 import org.junit.runner.RunWith
diff --git a/ui/ui-saved-instance-state/src/androidAndroidTest/kotlin/androidx/compose/runtime/savedinstancestate/Holder.kt b/compose/runtime/runtime-saved-instance-state/src/androidAndroidTest/kotlin/androidx/compose/runtime/savedinstancestate/Holder.kt
similarity index 100%
rename from ui/ui-saved-instance-state/src/androidAndroidTest/kotlin/androidx/compose/runtime/savedinstancestate/Holder.kt
rename to compose/runtime/runtime-saved-instance-state/src/androidAndroidTest/kotlin/androidx/compose/runtime/savedinstancestate/Holder.kt
diff --git a/ui/ui-saved-instance-state/src/androidAndroidTest/kotlin/androidx/compose/runtime/savedinstancestate/RememberSavedInstanceStateTest.kt b/compose/runtime/runtime-saved-instance-state/src/androidAndroidTest/kotlin/androidx/compose/runtime/savedinstancestate/RememberSavedInstanceStateTest.kt
similarity index 100%
rename from ui/ui-saved-instance-state/src/androidAndroidTest/kotlin/androidx/compose/runtime/savedinstancestate/RememberSavedInstanceStateTest.kt
rename to compose/runtime/runtime-saved-instance-state/src/androidAndroidTest/kotlin/androidx/compose/runtime/savedinstancestate/RememberSavedInstanceStateTest.kt
diff --git a/ui/ui-saved-instance-state/src/androidAndroidTest/kotlin/androidx/compose/runtime/savedinstancestate/RestorationInVariousScenariosTest.kt b/compose/runtime/runtime-saved-instance-state/src/androidAndroidTest/kotlin/androidx/compose/runtime/savedinstancestate/RestorationInVariousScenariosTest.kt
similarity index 100%
rename from ui/ui-saved-instance-state/src/androidAndroidTest/kotlin/androidx/compose/runtime/savedinstancestate/RestorationInVariousScenariosTest.kt
rename to compose/runtime/runtime-saved-instance-state/src/androidAndroidTest/kotlin/androidx/compose/runtime/savedinstancestate/RestorationInVariousScenariosTest.kt
diff --git a/ui/ui-saved-instance-state/src/androidAndroidTest/kotlin/androidx/compose/runtime/savedinstancestate/SavedInstanceStateTest.kt b/compose/runtime/runtime-saved-instance-state/src/androidAndroidTest/kotlin/androidx/compose/runtime/savedinstancestate/SavedInstanceStateTest.kt
similarity index 100%
rename from ui/ui-saved-instance-state/src/androidAndroidTest/kotlin/androidx/compose/runtime/savedinstancestate/SavedInstanceStateTest.kt
rename to compose/runtime/runtime-saved-instance-state/src/androidAndroidTest/kotlin/androidx/compose/runtime/savedinstancestate/SavedInstanceStateTest.kt
diff --git a/ui/ui-saved-instance-state/src/androidMain/AndroidManifest.xml b/compose/runtime/runtime-saved-instance-state/src/androidMain/AndroidManifest.xml
similarity index 100%
rename from ui/ui-saved-instance-state/src/androidMain/AndroidManifest.xml
rename to compose/runtime/runtime-saved-instance-state/src/androidMain/AndroidManifest.xml
diff --git a/ui/ui-saved-instance-state/src/androidTest/res/values/ids.xml b/compose/runtime/runtime-saved-instance-state/src/androidTest/res/values/ids.xml
similarity index 100%
rename from ui/ui-saved-instance-state/src/androidTest/res/values/ids.xml
rename to compose/runtime/runtime-saved-instance-state/src/androidTest/res/values/ids.xml
diff --git a/ui/ui-saved-instance-state/src/commonMain/kotlin/androidx/compose/runtime/savedinstancestate/ListSaver.kt b/compose/runtime/runtime-saved-instance-state/src/commonMain/kotlin/androidx/compose/runtime/savedinstancestate/ListSaver.kt
similarity index 100%
rename from ui/ui-saved-instance-state/src/commonMain/kotlin/androidx/compose/runtime/savedinstancestate/ListSaver.kt
rename to compose/runtime/runtime-saved-instance-state/src/commonMain/kotlin/androidx/compose/runtime/savedinstancestate/ListSaver.kt
diff --git a/ui/ui-saved-instance-state/src/commonMain/kotlin/androidx/compose/runtime/savedinstancestate/MapSaver.kt b/compose/runtime/runtime-saved-instance-state/src/commonMain/kotlin/androidx/compose/runtime/savedinstancestate/MapSaver.kt
similarity index 100%
rename from ui/ui-saved-instance-state/src/commonMain/kotlin/androidx/compose/runtime/savedinstancestate/MapSaver.kt
rename to compose/runtime/runtime-saved-instance-state/src/commonMain/kotlin/androidx/compose/runtime/savedinstancestate/MapSaver.kt
diff --git a/ui/ui-saved-instance-state/src/commonMain/kotlin/androidx/compose/runtime/savedinstancestate/RememberSavedInstanceState.kt b/compose/runtime/runtime-saved-instance-state/src/commonMain/kotlin/androidx/compose/runtime/savedinstancestate/RememberSavedInstanceState.kt
similarity index 100%
rename from ui/ui-saved-instance-state/src/commonMain/kotlin/androidx/compose/runtime/savedinstancestate/RememberSavedInstanceState.kt
rename to compose/runtime/runtime-saved-instance-state/src/commonMain/kotlin/androidx/compose/runtime/savedinstancestate/RememberSavedInstanceState.kt
diff --git a/ui/ui-saved-instance-state/src/commonMain/kotlin/androidx/compose/runtime/savedinstancestate/SavedInstanceState.kt b/compose/runtime/runtime-saved-instance-state/src/commonMain/kotlin/androidx/compose/runtime/savedinstancestate/SavedInstanceState.kt
similarity index 100%
rename from ui/ui-saved-instance-state/src/commonMain/kotlin/androidx/compose/runtime/savedinstancestate/SavedInstanceState.kt
rename to compose/runtime/runtime-saved-instance-state/src/commonMain/kotlin/androidx/compose/runtime/savedinstancestate/SavedInstanceState.kt
diff --git a/ui/ui-saved-instance-state/src/commonMain/kotlin/androidx/compose/runtime/savedinstancestate/Saver.kt b/compose/runtime/runtime-saved-instance-state/src/commonMain/kotlin/androidx/compose/runtime/savedinstancestate/Saver.kt
similarity index 100%
rename from ui/ui-saved-instance-state/src/commonMain/kotlin/androidx/compose/runtime/savedinstancestate/Saver.kt
rename to compose/runtime/runtime-saved-instance-state/src/commonMain/kotlin/androidx/compose/runtime/savedinstancestate/Saver.kt
diff --git a/ui/ui-saved-instance-state/src/commonMain/kotlin/androidx/compose/runtime/savedinstancestate/UiSavedStateRegistry.kt b/compose/runtime/runtime-saved-instance-state/src/commonMain/kotlin/androidx/compose/runtime/savedinstancestate/UiSavedStateRegistry.kt
similarity index 100%
rename from ui/ui-saved-instance-state/src/commonMain/kotlin/androidx/compose/runtime/savedinstancestate/UiSavedStateRegistry.kt
rename to compose/runtime/runtime-saved-instance-state/src/commonMain/kotlin/androidx/compose/runtime/savedinstancestate/UiSavedStateRegistry.kt
diff --git a/ui/ui-saved-instance-state/src/test/java/androidx/compose/runtime/savedinstancestate/AutoSaverTest.kt b/compose/runtime/runtime-saved-instance-state/src/test/java/androidx/compose/runtime/savedinstancestate/AutoSaverTest.kt
similarity index 100%
rename from ui/ui-saved-instance-state/src/test/java/androidx/compose/runtime/savedinstancestate/AutoSaverTest.kt
rename to compose/runtime/runtime-saved-instance-state/src/test/java/androidx/compose/runtime/savedinstancestate/AutoSaverTest.kt
diff --git a/ui/ui-saved-instance-state/src/test/java/androidx/compose/runtime/savedinstancestate/ListSaverTest.kt b/compose/runtime/runtime-saved-instance-state/src/test/java/androidx/compose/runtime/savedinstancestate/ListSaverTest.kt
similarity index 100%
rename from ui/ui-saved-instance-state/src/test/java/androidx/compose/runtime/savedinstancestate/ListSaverTest.kt
rename to compose/runtime/runtime-saved-instance-state/src/test/java/androidx/compose/runtime/savedinstancestate/ListSaverTest.kt
diff --git a/ui/ui-saved-instance-state/src/test/java/androidx/compose/runtime/savedinstancestate/MapSaverTest.kt b/compose/runtime/runtime-saved-instance-state/src/test/java/androidx/compose/runtime/savedinstancestate/MapSaverTest.kt
similarity index 100%
rename from ui/ui-saved-instance-state/src/test/java/androidx/compose/runtime/savedinstancestate/MapSaverTest.kt
rename to compose/runtime/runtime-saved-instance-state/src/test/java/androidx/compose/runtime/savedinstancestate/MapSaverTest.kt
diff --git a/ui/ui-saved-instance-state/src/test/java/androidx/compose/runtime/savedinstancestate/UiSavedStateRegistryTest.kt b/compose/runtime/runtime-saved-instance-state/src/test/java/androidx/compose/runtime/savedinstancestate/UiSavedStateRegistryTest.kt
similarity index 100%
rename from ui/ui-saved-instance-state/src/test/java/androidx/compose/runtime/savedinstancestate/UiSavedStateRegistryTest.kt
rename to compose/runtime/runtime-saved-instance-state/src/test/java/androidx/compose/runtime/savedinstancestate/UiSavedStateRegistryTest.kt
diff --git a/compose/compose-runtime/api/0.1.0-dev04.txt b/compose/runtime/runtime/api/0.1.0-dev04.txt
similarity index 100%
rename from compose/compose-runtime/api/0.1.0-dev04.txt
rename to compose/runtime/runtime/api/0.1.0-dev04.txt
diff --git a/compose/compose-runtime/api/0.1.0-dev05.txt b/compose/runtime/runtime/api/0.1.0-dev05.txt
similarity index 100%
rename from compose/compose-runtime/api/0.1.0-dev05.txt
rename to compose/runtime/runtime/api/0.1.0-dev05.txt
diff --git a/compose/compose-runtime/api/0.1.0-dev06.txt b/compose/runtime/runtime/api/0.1.0-dev06.txt
similarity index 100%
rename from compose/compose-runtime/api/0.1.0-dev06.txt
rename to compose/runtime/runtime/api/0.1.0-dev06.txt
diff --git a/compose/compose-runtime/api/0.1.0-dev07.txt b/compose/runtime/runtime/api/0.1.0-dev07.txt
similarity index 100%
rename from compose/compose-runtime/api/0.1.0-dev07.txt
rename to compose/runtime/runtime/api/0.1.0-dev07.txt
diff --git a/compose/compose-runtime/api/0.1.0-dev08.txt b/compose/runtime/runtime/api/0.1.0-dev08.txt
similarity index 100%
rename from compose/compose-runtime/api/0.1.0-dev08.txt
rename to compose/runtime/runtime/api/0.1.0-dev08.txt
diff --git a/compose/compose-runtime/api/0.1.0-dev09.txt b/compose/runtime/runtime/api/0.1.0-dev09.txt
similarity index 100%
rename from compose/compose-runtime/api/0.1.0-dev09.txt
rename to compose/runtime/runtime/api/0.1.0-dev09.txt
diff --git a/compose/compose-runtime/api/0.1.0-dev10.txt b/compose/runtime/runtime/api/0.1.0-dev10.txt
similarity index 100%
rename from compose/compose-runtime/api/0.1.0-dev10.txt
rename to compose/runtime/runtime/api/0.1.0-dev10.txt
diff --git a/compose/compose-runtime/api/0.1.0-dev11.txt b/compose/runtime/runtime/api/0.1.0-dev11.txt
similarity index 100%
rename from compose/compose-runtime/api/0.1.0-dev11.txt
rename to compose/runtime/runtime/api/0.1.0-dev11.txt
diff --git a/compose/compose-runtime/api/0.1.0-dev12.txt b/compose/runtime/runtime/api/0.1.0-dev12.txt
similarity index 100%
rename from compose/compose-runtime/api/0.1.0-dev12.txt
rename to compose/runtime/runtime/api/0.1.0-dev12.txt
diff --git a/compose/compose-runtime/api/0.1.0-dev14.txt b/compose/runtime/runtime/api/0.1.0-dev14.txt
similarity index 100%
rename from compose/compose-runtime/api/0.1.0-dev14.txt
rename to compose/runtime/runtime/api/0.1.0-dev14.txt
diff --git a/compose/compose-runtime/api/0.1.0-dev15.txt b/compose/runtime/runtime/api/0.1.0-dev15.txt
similarity index 100%
rename from compose/compose-runtime/api/0.1.0-dev15.txt
rename to compose/runtime/runtime/api/0.1.0-dev15.txt
diff --git a/compose/compose-runtime/api/0.1.0-dev16.txt b/compose/runtime/runtime/api/0.1.0-dev16.txt
similarity index 100%
rename from compose/compose-runtime/api/0.1.0-dev16.txt
rename to compose/runtime/runtime/api/0.1.0-dev16.txt
diff --git a/compose/compose-runtime/api/api_lint.ignore b/compose/runtime/runtime/api/api_lint.ignore
similarity index 100%
rename from compose/compose-runtime/api/api_lint.ignore
rename to compose/runtime/runtime/api/api_lint.ignore
diff --git a/compose/compose-runtime/api/current.txt b/compose/runtime/runtime/api/current.txt
similarity index 100%
rename from compose/compose-runtime/api/current.txt
rename to compose/runtime/runtime/api/current.txt
diff --git a/compose/compose-runtime/api/public_plus_experimental_0.1.0-dev04.txt b/compose/runtime/runtime/api/public_plus_experimental_0.1.0-dev04.txt
similarity index 100%
rename from compose/compose-runtime/api/public_plus_experimental_0.1.0-dev04.txt
rename to compose/runtime/runtime/api/public_plus_experimental_0.1.0-dev04.txt
diff --git a/compose/compose-runtime/api/public_plus_experimental_0.1.0-dev05.txt b/compose/runtime/runtime/api/public_plus_experimental_0.1.0-dev05.txt
similarity index 100%
rename from compose/compose-runtime/api/public_plus_experimental_0.1.0-dev05.txt
rename to compose/runtime/runtime/api/public_plus_experimental_0.1.0-dev05.txt
diff --git a/compose/compose-runtime/api/public_plus_experimental_0.1.0-dev06.txt b/compose/runtime/runtime/api/public_plus_experimental_0.1.0-dev06.txt
similarity index 100%
rename from compose/compose-runtime/api/public_plus_experimental_0.1.0-dev06.txt
rename to compose/runtime/runtime/api/public_plus_experimental_0.1.0-dev06.txt
diff --git a/compose/compose-runtime/api/public_plus_experimental_0.1.0-dev07.txt b/compose/runtime/runtime/api/public_plus_experimental_0.1.0-dev07.txt
similarity index 100%
rename from compose/compose-runtime/api/public_plus_experimental_0.1.0-dev07.txt
rename to compose/runtime/runtime/api/public_plus_experimental_0.1.0-dev07.txt
diff --git a/compose/compose-runtime/api/public_plus_experimental_0.1.0-dev08.txt b/compose/runtime/runtime/api/public_plus_experimental_0.1.0-dev08.txt
similarity index 100%
rename from compose/compose-runtime/api/public_plus_experimental_0.1.0-dev08.txt
rename to compose/runtime/runtime/api/public_plus_experimental_0.1.0-dev08.txt
diff --git a/compose/compose-runtime/api/public_plus_experimental_0.1.0-dev09.txt b/compose/runtime/runtime/api/public_plus_experimental_0.1.0-dev09.txt
similarity index 100%
rename from compose/compose-runtime/api/public_plus_experimental_0.1.0-dev09.txt
rename to compose/runtime/runtime/api/public_plus_experimental_0.1.0-dev09.txt
diff --git a/compose/compose-runtime/api/public_plus_experimental_0.1.0-dev10.txt b/compose/runtime/runtime/api/public_plus_experimental_0.1.0-dev10.txt
similarity index 100%
rename from compose/compose-runtime/api/public_plus_experimental_0.1.0-dev10.txt
rename to compose/runtime/runtime/api/public_plus_experimental_0.1.0-dev10.txt
diff --git a/compose/compose-runtime/api/public_plus_experimental_0.1.0-dev11.txt b/compose/runtime/runtime/api/public_plus_experimental_0.1.0-dev11.txt
similarity index 100%
rename from compose/compose-runtime/api/public_plus_experimental_0.1.0-dev11.txt
rename to compose/runtime/runtime/api/public_plus_experimental_0.1.0-dev11.txt
diff --git a/compose/compose-runtime/api/public_plus_experimental_0.1.0-dev12.txt b/compose/runtime/runtime/api/public_plus_experimental_0.1.0-dev12.txt
similarity index 100%
rename from compose/compose-runtime/api/public_plus_experimental_0.1.0-dev12.txt
rename to compose/runtime/runtime/api/public_plus_experimental_0.1.0-dev12.txt
diff --git a/compose/compose-runtime/api/public_plus_experimental_0.1.0-dev14.txt b/compose/runtime/runtime/api/public_plus_experimental_0.1.0-dev14.txt
similarity index 100%
rename from compose/compose-runtime/api/public_plus_experimental_0.1.0-dev14.txt
rename to compose/runtime/runtime/api/public_plus_experimental_0.1.0-dev14.txt
diff --git a/compose/compose-runtime/api/public_plus_experimental_0.1.0-dev15.txt b/compose/runtime/runtime/api/public_plus_experimental_0.1.0-dev15.txt
similarity index 100%
rename from compose/compose-runtime/api/public_plus_experimental_0.1.0-dev15.txt
rename to compose/runtime/runtime/api/public_plus_experimental_0.1.0-dev15.txt
diff --git a/compose/compose-runtime/api/public_plus_experimental_0.1.0-dev16.txt b/compose/runtime/runtime/api/public_plus_experimental_0.1.0-dev16.txt
similarity index 100%
rename from compose/compose-runtime/api/public_plus_experimental_0.1.0-dev16.txt
rename to compose/runtime/runtime/api/public_plus_experimental_0.1.0-dev16.txt
diff --git a/compose/compose-runtime/api/public_plus_experimental_current.txt b/compose/runtime/runtime/api/public_plus_experimental_current.txt
similarity index 100%
rename from compose/compose-runtime/api/public_plus_experimental_current.txt
rename to compose/runtime/runtime/api/public_plus_experimental_current.txt
diff --git a/compose/compose-runtime/api/res-0.1.0-dev04.txt b/compose/runtime/runtime/api/res-0.1.0-dev04.txt
similarity index 100%
rename from compose/compose-runtime/api/res-0.1.0-dev04.txt
rename to compose/runtime/runtime/api/res-0.1.0-dev04.txt
diff --git a/compose/compose-runtime/api/res-0.1.0-dev05.txt b/compose/runtime/runtime/api/res-0.1.0-dev05.txt
similarity index 100%
rename from compose/compose-runtime/api/res-0.1.0-dev05.txt
rename to compose/runtime/runtime/api/res-0.1.0-dev05.txt
diff --git a/compose/compose-runtime/api/res-0.1.0-dev06.txt b/compose/runtime/runtime/api/res-0.1.0-dev06.txt
similarity index 100%
rename from compose/compose-runtime/api/res-0.1.0-dev06.txt
rename to compose/runtime/runtime/api/res-0.1.0-dev06.txt
diff --git a/compose/compose-runtime/api/res-0.1.0-dev07.txt b/compose/runtime/runtime/api/res-0.1.0-dev07.txt
similarity index 100%
rename from compose/compose-runtime/api/res-0.1.0-dev07.txt
rename to compose/runtime/runtime/api/res-0.1.0-dev07.txt
diff --git a/compose/compose-runtime/api/res-0.1.0-dev08.txt b/compose/runtime/runtime/api/res-0.1.0-dev08.txt
similarity index 100%
rename from compose/compose-runtime/api/res-0.1.0-dev08.txt
rename to compose/runtime/runtime/api/res-0.1.0-dev08.txt
diff --git a/compose/compose-runtime/api/res-0.1.0-dev09.txt b/compose/runtime/runtime/api/res-0.1.0-dev09.txt
similarity index 100%
rename from compose/compose-runtime/api/res-0.1.0-dev09.txt
rename to compose/runtime/runtime/api/res-0.1.0-dev09.txt
diff --git a/compose/compose-runtime/api/res-0.1.0-dev10.txt b/compose/runtime/runtime/api/res-0.1.0-dev10.txt
similarity index 100%
rename from compose/compose-runtime/api/res-0.1.0-dev10.txt
rename to compose/runtime/runtime/api/res-0.1.0-dev10.txt
diff --git a/compose/compose-runtime/api/res-0.1.0-dev11.txt b/compose/runtime/runtime/api/res-0.1.0-dev11.txt
similarity index 100%
rename from compose/compose-runtime/api/res-0.1.0-dev11.txt
rename to compose/runtime/runtime/api/res-0.1.0-dev11.txt
diff --git a/compose/compose-runtime/api/res-0.1.0-dev12.txt b/compose/runtime/runtime/api/res-0.1.0-dev12.txt
similarity index 100%
rename from compose/compose-runtime/api/res-0.1.0-dev12.txt
rename to compose/runtime/runtime/api/res-0.1.0-dev12.txt
diff --git a/compose/compose-runtime/api/res-0.1.0-dev14.txt b/compose/runtime/runtime/api/res-0.1.0-dev14.txt
similarity index 100%
rename from compose/compose-runtime/api/res-0.1.0-dev14.txt
rename to compose/runtime/runtime/api/res-0.1.0-dev14.txt
diff --git a/compose/compose-runtime/api/res-0.1.0-dev15.txt b/compose/runtime/runtime/api/res-0.1.0-dev15.txt
similarity index 100%
rename from compose/compose-runtime/api/res-0.1.0-dev15.txt
rename to compose/runtime/runtime/api/res-0.1.0-dev15.txt
diff --git a/compose/compose-runtime/api/res-0.1.0-dev16.txt b/compose/runtime/runtime/api/res-0.1.0-dev16.txt
similarity index 100%
rename from compose/compose-runtime/api/res-0.1.0-dev16.txt
rename to compose/runtime/runtime/api/res-0.1.0-dev16.txt
diff --git a/compose/compose-runtime/api/res-current.txt b/compose/runtime/runtime/api/res-current.txt
similarity index 100%
rename from compose/compose-runtime/api/res-current.txt
rename to compose/runtime/runtime/api/res-current.txt
diff --git a/compose/compose-runtime/api/restricted_0.1.0-dev04.txt b/compose/runtime/runtime/api/restricted_0.1.0-dev04.txt
similarity index 100%
rename from compose/compose-runtime/api/restricted_0.1.0-dev04.txt
rename to compose/runtime/runtime/api/restricted_0.1.0-dev04.txt
diff --git a/compose/compose-runtime/api/restricted_0.1.0-dev05.txt b/compose/runtime/runtime/api/restricted_0.1.0-dev05.txt
similarity index 100%
rename from compose/compose-runtime/api/restricted_0.1.0-dev05.txt
rename to compose/runtime/runtime/api/restricted_0.1.0-dev05.txt
diff --git a/compose/compose-runtime/api/restricted_0.1.0-dev06.txt b/compose/runtime/runtime/api/restricted_0.1.0-dev06.txt
similarity index 100%
rename from compose/compose-runtime/api/restricted_0.1.0-dev06.txt
rename to compose/runtime/runtime/api/restricted_0.1.0-dev06.txt
diff --git a/compose/compose-runtime/api/restricted_0.1.0-dev07.txt b/compose/runtime/runtime/api/restricted_0.1.0-dev07.txt
similarity index 100%
rename from compose/compose-runtime/api/restricted_0.1.0-dev07.txt
rename to compose/runtime/runtime/api/restricted_0.1.0-dev07.txt
diff --git a/compose/compose-runtime/api/restricted_0.1.0-dev08.txt b/compose/runtime/runtime/api/restricted_0.1.0-dev08.txt
similarity index 100%
rename from compose/compose-runtime/api/restricted_0.1.0-dev08.txt
rename to compose/runtime/runtime/api/restricted_0.1.0-dev08.txt
diff --git a/compose/compose-runtime/api/restricted_0.1.0-dev09.txt b/compose/runtime/runtime/api/restricted_0.1.0-dev09.txt
similarity index 100%
rename from compose/compose-runtime/api/restricted_0.1.0-dev09.txt
rename to compose/runtime/runtime/api/restricted_0.1.0-dev09.txt
diff --git a/compose/compose-runtime/api/restricted_0.1.0-dev10.txt b/compose/runtime/runtime/api/restricted_0.1.0-dev10.txt
similarity index 100%
rename from compose/compose-runtime/api/restricted_0.1.0-dev10.txt
rename to compose/runtime/runtime/api/restricted_0.1.0-dev10.txt
diff --git a/compose/compose-runtime/api/restricted_0.1.0-dev11.txt b/compose/runtime/runtime/api/restricted_0.1.0-dev11.txt
similarity index 100%
rename from compose/compose-runtime/api/restricted_0.1.0-dev11.txt
rename to compose/runtime/runtime/api/restricted_0.1.0-dev11.txt
diff --git a/compose/compose-runtime/api/restricted_0.1.0-dev12.txt b/compose/runtime/runtime/api/restricted_0.1.0-dev12.txt
similarity index 100%
rename from compose/compose-runtime/api/restricted_0.1.0-dev12.txt
rename to compose/runtime/runtime/api/restricted_0.1.0-dev12.txt
diff --git a/compose/compose-runtime/api/restricted_0.1.0-dev14.txt b/compose/runtime/runtime/api/restricted_0.1.0-dev14.txt
similarity index 100%
rename from compose/compose-runtime/api/restricted_0.1.0-dev14.txt
rename to compose/runtime/runtime/api/restricted_0.1.0-dev14.txt
diff --git a/compose/compose-runtime/api/restricted_0.1.0-dev15.txt b/compose/runtime/runtime/api/restricted_0.1.0-dev15.txt
similarity index 100%
rename from compose/compose-runtime/api/restricted_0.1.0-dev15.txt
rename to compose/runtime/runtime/api/restricted_0.1.0-dev15.txt
diff --git a/compose/compose-runtime/api/restricted_0.1.0-dev16.txt b/compose/runtime/runtime/api/restricted_0.1.0-dev16.txt
similarity index 100%
rename from compose/compose-runtime/api/restricted_0.1.0-dev16.txt
rename to compose/runtime/runtime/api/restricted_0.1.0-dev16.txt
diff --git a/compose/compose-runtime/api/restricted_current.txt b/compose/runtime/runtime/api/restricted_current.txt
similarity index 99%
rename from compose/compose-runtime/api/restricted_current.txt
rename to compose/runtime/runtime/api/restricted_current.txt
index 4bf593f..bb35e2b 100644
--- a/compose/compose-runtime/api/restricted_current.txt
+++ b/compose/runtime/runtime/api/restricted_current.txt
@@ -946,6 +946,7 @@
     method public static inline <T extends androidx.compose.runtime.snapshots.StateRecord, R> R! writable(T, androidx.compose.runtime.snapshots.StateObject state, kotlin.jvm.functions.Function1<? super T,? extends R> block);
     method @kotlin.PublishedApi internal static <T extends androidx.compose.runtime.snapshots.StateRecord> T writableRecord(T, androidx.compose.runtime.snapshots.StateObject state, androidx.compose.runtime.snapshots.Snapshot snapshot);
     field @kotlin.PublishedApi internal static final Object lock;
+    field @kotlin.PublishedApi internal static final androidx.compose.runtime.snapshots.Snapshot snapshotInitializer;
   }
 
   @androidx.compose.runtime.Stable public final class SnapshotStateList<T> implements kotlin.jvm.internal.markers.KMutableList java.util.List<T> androidx.compose.runtime.snapshots.StateObject {
diff --git a/compose/compose-runtime/build.gradle b/compose/runtime/runtime/build.gradle
similarity index 100%
rename from compose/compose-runtime/build.gradle
rename to compose/runtime/runtime/build.gradle
diff --git a/compose/compose-runtime/compose-runtime-benchmark/build.gradle b/compose/runtime/runtime/compose-runtime-benchmark/build.gradle
similarity index 100%
rename from compose/compose-runtime/compose-runtime-benchmark/build.gradle
rename to compose/runtime/runtime/compose-runtime-benchmark/build.gradle
diff --git a/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/AndroidManifest.xml b/compose/runtime/runtime/compose-runtime-benchmark/src/androidTest/AndroidManifest.xml
similarity index 100%
rename from compose/compose-runtime/compose-runtime-benchmark/src/androidTest/AndroidManifest.xml
rename to compose/runtime/runtime/compose-runtime-benchmark/src/androidTest/AndroidManifest.xml
diff --git a/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/HotReloadIntegrationTests.kt b/compose/runtime/runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/HotReloadIntegrationTests.kt
similarity index 100%
rename from compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/HotReloadIntegrationTests.kt
rename to compose/runtime/runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/HotReloadIntegrationTests.kt
diff --git a/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/ComposeActivity.kt b/compose/runtime/runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/ComposeActivity.kt
similarity index 100%
rename from compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/ComposeActivity.kt
rename to compose/runtime/runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/ComposeActivity.kt
diff --git a/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/ComposeBenchmark.kt b/compose/runtime/runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/ComposeBenchmark.kt
similarity index 99%
rename from compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/ComposeBenchmark.kt
rename to compose/runtime/runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/ComposeBenchmark.kt
index c119dfe..8d9c251 100644
--- a/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/ComposeBenchmark.kt
+++ b/compose/runtime/runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/ComposeBenchmark.kt
@@ -21,11 +21,11 @@
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.padding
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.benchmark.realworld4.RealWorld4_FancyWidget_000
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
-import androidx.compose.runtime.benchmark.realworld4.RealWorld4_FancyWidget_000
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.composed
 import androidx.compose.ui.graphics.Color
diff --git a/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/ComposeBenchmarkBase.kt b/compose/runtime/runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/ComposeBenchmarkBase.kt
similarity index 100%
rename from compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/ComposeBenchmarkBase.kt
rename to compose/runtime/runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/ComposeBenchmarkBase.kt
diff --git a/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/DbMonsterBenchmark.kt b/compose/runtime/runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/DbMonsterBenchmark.kt
similarity index 99%
rename from compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/DbMonsterBenchmark.kt
rename to compose/runtime/runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/DbMonsterBenchmark.kt
index 4feea7a..f9029f1 100644
--- a/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/DbMonsterBenchmark.kt
+++ b/compose/runtime/runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/DbMonsterBenchmark.kt
@@ -16,14 +16,14 @@
 
 package androidx.compose.runtime.benchmark
 
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxHeight
 import androidx.compose.runtime.benchmark.dbmonster.DatabaseList
 import androidx.compose.runtime.benchmark.dbmonster.DatabaseRow
+import androidx.compose.ui.Modifier
 import androidx.test.annotation.UiThreadTest
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
-import androidx.compose.ui.Modifier
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.fillMaxHeight
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
diff --git a/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/DeepTreeBenchmark.kt b/compose/runtime/runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/DeepTreeBenchmark.kt
similarity index 100%
rename from compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/DeepTreeBenchmark.kt
rename to compose/runtime/runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/DeepTreeBenchmark.kt
diff --git a/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/SiblingBenchmark.kt b/compose/runtime/runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/SiblingBenchmark.kt
similarity index 100%
rename from compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/SiblingBenchmark.kt
rename to compose/runtime/runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/SiblingBenchmark.kt
diff --git a/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/SnapshotStateObserverBenchmark.kt b/compose/runtime/runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/SnapshotStateObserverBenchmark.kt
similarity index 100%
rename from compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/SnapshotStateObserverBenchmark.kt
rename to compose/runtime/runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/SnapshotStateObserverBenchmark.kt
diff --git a/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/collection/MutableVectorBenchmark.kt b/compose/runtime/runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/collection/MutableVectorBenchmark.kt
similarity index 99%
rename from compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/collection/MutableVectorBenchmark.kt
rename to compose/runtime/runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/collection/MutableVectorBenchmark.kt
index f252e03..7059121 100644
--- a/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/collection/MutableVectorBenchmark.kt
+++ b/compose/runtime/runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/collection/MutableVectorBenchmark.kt
@@ -21,11 +21,11 @@
 import androidx.compose.runtime.collection.ExperimentalCollectionApi
 import androidx.compose.runtime.collection.MutableVector
 import androidx.compose.runtime.collection.mutableVectorOf
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.LargeTest
 import androidx.compose.ui.util.fastAny
 import androidx.compose.ui.util.fastForEach
 import androidx.compose.ui.util.fastSumBy
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
 import org.junit.Before
 import org.junit.FixMethodOrder
 import org.junit.Test
diff --git a/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/dbmonster/DbMonster.kt b/compose/runtime/runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/dbmonster/DbMonster.kt
similarity index 99%
rename from compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/dbmonster/DbMonster.kt
rename to compose/runtime/runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/dbmonster/DbMonster.kt
index 5b2df5a5..c993323 100644
--- a/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/dbmonster/DbMonster.kt
+++ b/compose/runtime/runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/dbmonster/DbMonster.kt
@@ -16,16 +16,16 @@
 
 package androidx.compose.runtime.benchmark.dbmonster
 
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.Modifier
 import androidx.compose.foundation.Text
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.fillMaxHeight
 import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
 import kotlin.random.Random
 
 private fun randomQuery(random: Random): String = random.nextDouble().let {
diff --git a/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/deeptree/DeepTree.kt b/compose/runtime/runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/deeptree/DeepTree.kt
similarity index 99%
rename from compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/deeptree/DeepTree.kt
rename to compose/runtime/runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/deeptree/DeepTree.kt
index 31c3719..1f941ef 100644
--- a/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/deeptree/DeepTree.kt
+++ b/compose/runtime/runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/deeptree/DeepTree.kt
@@ -16,16 +16,16 @@
 
 package androidx.compose.runtime.benchmark.deeptree
 
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
 import androidx.compose.foundation.Box
 import androidx.compose.foundation.background
-import androidx.compose.ui.graphics.Color
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.fillMaxHeight
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
 
 val blueBackground = Modifier.background(color = Color.Blue)
 val magentaBackground = Modifier.background(color = Color.Magenta)
diff --git a/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/realworld4/README.md b/compose/runtime/runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/realworld4/README.md
similarity index 100%
rename from compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/realworld4/README.md
rename to compose/runtime/runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/realworld4/README.md
diff --git a/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/realworld4/RealWorld4_DataModels.kt b/compose/runtime/runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/realworld4/RealWorld4_DataModels.kt
similarity index 100%
rename from compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/realworld4/RealWorld4_DataModels.kt
rename to compose/runtime/runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/realworld4/RealWorld4_DataModels.kt
diff --git a/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/realworld4/RealWorld4_UnmemoizablePojos.kt b/compose/runtime/runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/realworld4/RealWorld4_UnmemoizablePojos.kt
similarity index 100%
rename from compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/realworld4/RealWorld4_UnmemoizablePojos.kt
rename to compose/runtime/runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/realworld4/RealWorld4_UnmemoizablePojos.kt
diff --git a/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/realworld4/RealWorld4_Utilities.kt b/compose/runtime/runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/realworld4/RealWorld4_Utilities.kt
similarity index 100%
rename from compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/realworld4/RealWorld4_Utilities.kt
rename to compose/runtime/runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/realworld4/RealWorld4_Utilities.kt
diff --git a/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/realworld4/RealWorld4_Widgets.kt b/compose/runtime/runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/realworld4/RealWorld4_Widgets.kt
similarity index 99%
rename from compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/realworld4/RealWorld4_Widgets.kt
rename to compose/runtime/runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/realworld4/RealWorld4_Widgets.kt
index 9412ad6..2513649 100644
--- a/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/realworld4/RealWorld4_Widgets.kt
+++ b/compose/runtime/runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/realworld4/RealWorld4_Widgets.kt
@@ -24,17 +24,17 @@
  * large scale (eg. gmail-sized application).
  */
 
+import androidx.compose.foundation.Box
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.WithConstraints
 import androidx.compose.ui.graphics.Color
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.Box
-import androidx.compose.foundation.background
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.ui.unit.dp
 import kotlin.reflect.KCallable
 import kotlin.reflect.full.memberProperties
diff --git a/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/siblings/SiblingManagement.kt b/compose/runtime/runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/siblings/SiblingManagement.kt
similarity index 99%
rename from compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/siblings/SiblingManagement.kt
rename to compose/runtime/runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/siblings/SiblingManagement.kt
index 8acc8b5..8b69b025 100644
--- a/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/siblings/SiblingManagement.kt
+++ b/compose/runtime/runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/runtime/benchmark/siblings/SiblingManagement.kt
@@ -16,18 +16,18 @@
 
 package androidx.compose.runtime.benchmark.siblings
 
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.key
-import androidx.compose.ui.Modifier
 import androidx.compose.foundation.Box
 import androidx.compose.foundation.Text
 import androidx.compose.foundation.background
-import androidx.compose.ui.graphics.Color
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.fillMaxHeight
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.key
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.text.TextStyle
 import kotlin.random.Random
 
diff --git a/compose/compose-runtime/compose-runtime-benchmark/src/main/AndroidManifest.xml b/compose/runtime/runtime/compose-runtime-benchmark/src/main/AndroidManifest.xml
similarity index 100%
rename from compose/compose-runtime/compose-runtime-benchmark/src/main/AndroidManifest.xml
rename to compose/runtime/runtime/compose-runtime-benchmark/src/main/AndroidManifest.xml
diff --git a/compose/compose-runtime/lint-baseline.xml b/compose/runtime/runtime/lint-baseline.xml
similarity index 100%
rename from compose/compose-runtime/lint-baseline.xml
rename to compose/runtime/runtime/lint-baseline.xml
diff --git a/compose/compose-runtime/samples/build.gradle b/compose/runtime/runtime/samples/build.gradle
similarity index 100%
rename from compose/compose-runtime/samples/build.gradle
rename to compose/runtime/runtime/samples/build.gradle
diff --git a/compose/compose-runtime/samples/src/main/AndroidManifest.xml b/compose/runtime/runtime/samples/src/main/AndroidManifest.xml
similarity index 100%
rename from compose/compose-runtime/samples/src/main/AndroidManifest.xml
rename to compose/runtime/runtime/samples/src/main/AndroidManifest.xml
diff --git a/compose/compose-runtime/samples/src/main/java/androidx/compose/runtime/samples/AmbientSamples.kt b/compose/runtime/runtime/samples/src/main/java/androidx/compose/runtime/samples/AmbientSamples.kt
similarity index 100%
rename from compose/compose-runtime/samples/src/main/java/androidx/compose/runtime/samples/AmbientSamples.kt
rename to compose/runtime/runtime/samples/src/main/java/androidx/compose/runtime/samples/AmbientSamples.kt
diff --git a/compose/compose-runtime/samples/src/main/java/androidx/compose/runtime/samples/CustomTreeCompositionSamples.kt b/compose/runtime/runtime/samples/src/main/java/androidx/compose/runtime/samples/CustomTreeCompositionSamples.kt
similarity index 100%
rename from compose/compose-runtime/samples/src/main/java/androidx/compose/runtime/samples/CustomTreeCompositionSamples.kt
rename to compose/runtime/runtime/samples/src/main/java/androidx/compose/runtime/samples/CustomTreeCompositionSamples.kt
diff --git a/compose/compose-runtime/samples/src/main/java/androidx/compose/runtime/samples/EffectSamples.kt b/compose/runtime/runtime/samples/src/main/java/androidx/compose/runtime/samples/EffectSamples.kt
similarity index 100%
rename from compose/compose-runtime/samples/src/main/java/androidx/compose/runtime/samples/EffectSamples.kt
rename to compose/runtime/runtime/samples/src/main/java/androidx/compose/runtime/samples/EffectSamples.kt
diff --git a/compose/compose-runtime/samples/src/main/java/androidx/compose/runtime/samples/FlowAdapterSamples.kt b/compose/runtime/runtime/samples/src/main/java/androidx/compose/runtime/samples/FlowAdapterSamples.kt
similarity index 99%
rename from compose/compose-runtime/samples/src/main/java/androidx/compose/runtime/samples/FlowAdapterSamples.kt
rename to compose/runtime/runtime/samples/src/main/java/androidx/compose/runtime/samples/FlowAdapterSamples.kt
index bc42ebd..64a0a52 100644
--- a/compose/compose-runtime/samples/src/main/java/androidx/compose/runtime/samples/FlowAdapterSamples.kt
+++ b/compose/runtime/runtime/samples/src/main/java/androidx/compose/runtime/samples/FlowAdapterSamples.kt
@@ -17,10 +17,10 @@
 package androidx.compose.runtime.samples
 
 import androidx.annotation.Sampled
+import androidx.compose.foundation.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
-import androidx.compose.foundation.Text
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.StateFlow
diff --git a/compose/compose-runtime/samples/src/main/java/androidx/compose/runtime/samples/ImmutableSamples.kt b/compose/runtime/runtime/samples/src/main/java/androidx/compose/runtime/samples/ImmutableSamples.kt
similarity index 99%
rename from compose/compose-runtime/samples/src/main/java/androidx/compose/runtime/samples/ImmutableSamples.kt
rename to compose/runtime/runtime/samples/src/main/java/androidx/compose/runtime/samples/ImmutableSamples.kt
index 6293e58..2d91ae0 100644
--- a/compose/compose-runtime/samples/src/main/java/androidx/compose/runtime/samples/ImmutableSamples.kt
+++ b/compose/runtime/runtime/samples/src/main/java/androidx/compose/runtime/samples/ImmutableSamples.kt
@@ -17,11 +17,11 @@
 package androidx.compose.runtime.samples
 
 import androidx.annotation.Sampled
+import androidx.compose.foundation.Text
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Immutable
-import androidx.compose.foundation.Text
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.Column
 
 @Sampled
 @Composable
diff --git a/compose/compose-runtime/samples/src/main/java/androidx/compose/runtime/samples/KeySamples.kt b/compose/runtime/runtime/samples/src/main/java/androidx/compose/runtime/samples/KeySamples.kt
similarity index 100%
rename from compose/compose-runtime/samples/src/main/java/androidx/compose/runtime/samples/KeySamples.kt
rename to compose/runtime/runtime/samples/src/main/java/androidx/compose/runtime/samples/KeySamples.kt
diff --git a/compose/compose-runtime/samples/src/main/java/androidx/compose/runtime/samples/ModelSamples.kt b/compose/runtime/runtime/samples/src/main/java/androidx/compose/runtime/samples/ModelSamples.kt
similarity index 100%
rename from compose/compose-runtime/samples/src/main/java/androidx/compose/runtime/samples/ModelSamples.kt
rename to compose/runtime/runtime/samples/src/main/java/androidx/compose/runtime/samples/ModelSamples.kt
diff --git a/compose/compose-runtime/samples/src/main/java/androidx/compose/runtime/samples/MutableStateListSamples.kt b/compose/runtime/runtime/samples/src/main/java/androidx/compose/runtime/samples/MutableStateListSamples.kt
similarity index 100%
rename from compose/compose-runtime/samples/src/main/java/androidx/compose/runtime/samples/MutableStateListSamples.kt
rename to compose/runtime/runtime/samples/src/main/java/androidx/compose/runtime/samples/MutableStateListSamples.kt
diff --git a/compose/compose-runtime/samples/src/main/java/androidx/compose/runtime/samples/MutableStateMapSample.kt b/compose/runtime/runtime/samples/src/main/java/androidx/compose/runtime/samples/MutableStateMapSample.kt
similarity index 100%
rename from compose/compose-runtime/samples/src/main/java/androidx/compose/runtime/samples/MutableStateMapSample.kt
rename to compose/runtime/runtime/samples/src/main/java/androidx/compose/runtime/samples/MutableStateMapSample.kt
diff --git a/compose/compose-runtime/samples/src/main/java/androidx/compose/runtime/samples/SharedSamples.kt b/compose/runtime/runtime/samples/src/main/java/androidx/compose/runtime/samples/SharedSamples.kt
similarity index 100%
rename from compose/compose-runtime/samples/src/main/java/androidx/compose/runtime/samples/SharedSamples.kt
rename to compose/runtime/runtime/samples/src/main/java/androidx/compose/runtime/samples/SharedSamples.kt
diff --git a/compose/compose-runtime/samples/src/main/java/androidx/compose/runtime/samples/SlotTableSamples.kt b/compose/runtime/runtime/samples/src/main/java/androidx/compose/runtime/samples/SlotTableSamples.kt
similarity index 100%
rename from compose/compose-runtime/samples/src/main/java/androidx/compose/runtime/samples/SlotTableSamples.kt
rename to compose/runtime/runtime/samples/src/main/java/androidx/compose/runtime/samples/SlotTableSamples.kt
diff --git a/compose/compose-runtime/samples/src/main/java/androidx/compose/runtime/samples/SnapshotMutationPolicySamples.kt b/compose/runtime/runtime/samples/src/main/java/androidx/compose/runtime/samples/SnapshotMutationPolicySamples.kt
similarity index 100%
rename from compose/compose-runtime/samples/src/main/java/androidx/compose/runtime/samples/SnapshotMutationPolicySamples.kt
rename to compose/runtime/runtime/samples/src/main/java/androidx/compose/runtime/samples/SnapshotMutationPolicySamples.kt
diff --git a/compose/compose-runtime/src/androidAndroidTest/AndroidManifest.xml b/compose/runtime/runtime/src/androidAndroidTest/AndroidManifest.xml
similarity index 100%
rename from compose/compose-runtime/src/androidAndroidTest/AndroidManifest.xml
rename to compose/runtime/runtime/src/androidAndroidTest/AndroidManifest.xml
diff --git a/compose/compose-runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/AmbientTests.kt b/compose/runtime/runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/AmbientTests.kt
similarity index 99%
rename from compose/compose-runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/AmbientTests.kt
rename to compose/runtime/runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/AmbientTests.kt
index 67114f3..5d51473 100644
--- a/compose/compose-runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/AmbientTests.kt
+++ b/compose/runtime/runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/AmbientTests.kt
@@ -18,12 +18,12 @@
 package androidx.compose.runtime
 
 import android.widget.TextView
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.MediumTest
 import androidx.compose.ui.node.ExperimentalLayoutNodeApi
 import androidx.compose.ui.node.LayoutNode
 import androidx.compose.ui.platform.subcomposeInto
 import androidx.compose.ui.viewinterop.emitView
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
 import org.junit.After
 import org.junit.Rule
 import org.junit.Test
diff --git a/compose/runtime/runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/AndroidSnapshotTests.kt b/compose/runtime/runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/AndroidSnapshotTests.kt
new file mode 100644
index 0000000..7a3b9ac
--- /dev/null
+++ b/compose/runtime/runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/AndroidSnapshotTests.kt
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2020 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.runtime
+
+import androidx.compose.runtime.snapshots.Snapshot
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+class AndroidSnapshotTests : BaseComposeTest() {
+    @get:Rule
+    override val activityRule = makeTestActivityRule()
+
+    @OptIn(ExperimentalComposeApi::class)
+    @Test // regression test for b/163903673
+    fun testCommittingInABackgroundThread() {
+        val states = Array(10000) { mutableStateOf(0) }
+        var stop = false
+        object : Thread() {
+            override fun run() {
+                while (!stop) {
+                    for (state in states) {
+                        state.value = state.value + 1
+                    }
+                    sleep(1)
+                }
+            }
+        }.start()
+        try {
+            val unregister = Snapshot.registerApplyObserver { changed, _ ->
+                // Try to catch a concurrent modification exception
+                val iterator = changed.iterator()
+                while (iterator.hasNext()) {
+                    iterator.next()
+                }
+            }
+            try {
+                repeat(1000) {
+                    activityRule.activity.uiThread {
+                        Snapshot.sendApplyNotifications()
+                    }
+                }
+            } finally {
+                unregister()
+            }
+        } finally {
+            stop = true
+        }
+    }
+}
\ No newline at end of file
diff --git a/compose/compose-runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/BaseComposeTest.kt b/compose/runtime/runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/BaseComposeTest.kt
similarity index 99%
rename from compose/compose-runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/BaseComposeTest.kt
rename to compose/runtime/runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/BaseComposeTest.kt
index 8f2bd65..cdacd935 100644
--- a/compose/compose-runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/BaseComposeTest.kt
+++ b/compose/runtime/runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/BaseComposeTest.kt
@@ -17,20 +17,20 @@
 @file:Suppress("PLUGIN_ERROR")
 package androidx.compose.runtime
 
-import android.os.Bundle
-import android.widget.LinearLayout
-import java.util.concurrent.CountDownLatch
-import java.util.concurrent.TimeUnit
-import kotlin.test.assertTrue
 import android.app.Activity
+import android.os.Bundle
 import android.os.Looper
 import android.view.ViewGroup
+import android.widget.LinearLayout
 import androidx.compose.runtime.snapshots.Snapshot
 import androidx.compose.ui.node.ExperimentalLayoutNodeApi
 import androidx.compose.ui.node.LayoutNode
 import androidx.compose.ui.platform.ContextAmbient
 import androidx.compose.ui.platform.setViewContent
 import androidx.compose.ui.platform.subcomposeInto
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
+import kotlin.test.assertTrue
 
 class TestActivity : Activity() {
     override fun onCreate(savedInstanceState: Bundle?) {
diff --git a/compose/compose-runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/ComposeIntoTests.kt b/compose/runtime/runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/ComposeIntoTests.kt
similarity index 100%
rename from compose/compose-runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/ComposeIntoTests.kt
rename to compose/runtime/runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/ComposeIntoTests.kt
diff --git a/compose/compose-runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/ComposeModelTests.kt b/compose/runtime/runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/ComposeModelTests.kt
similarity index 100%
rename from compose/compose-runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/ComposeModelTests.kt
rename to compose/runtime/runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/ComposeModelTests.kt
diff --git a/compose/compose-runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/CompoundHashKeyTests.kt b/compose/runtime/runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/CompoundHashKeyTests.kt
similarity index 99%
rename from compose/compose-runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/CompoundHashKeyTests.kt
rename to compose/runtime/runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/CompoundHashKeyTests.kt
index b8c38ca..3633e3b 100644
--- a/compose/compose-runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/CompoundHashKeyTests.kt
+++ b/compose/runtime/runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/CompoundHashKeyTests.kt
@@ -20,8 +20,8 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
 import org.junit.Rule
-import org.junit.runner.RunWith
 import org.junit.Test
+import org.junit.runner.RunWith
 import kotlin.test.assertEquals
 import kotlin.test.assertNotEquals
 
diff --git a/compose/compose-runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/DisposeTests.kt b/compose/runtime/runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/DisposeTests.kt
similarity index 100%
rename from compose/compose-runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/DisposeTests.kt
rename to compose/runtime/runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/DisposeTests.kt
diff --git a/compose/compose-runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/EffectsTests.kt b/compose/runtime/runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/EffectsTests.kt
similarity index 100%
rename from compose/compose-runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/EffectsTests.kt
rename to compose/runtime/runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/EffectsTests.kt
diff --git a/compose/compose-runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/EmittableComposer.kt b/compose/runtime/runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/EmittableComposer.kt
similarity index 99%
rename from compose/compose-runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/EmittableComposer.kt
rename to compose/runtime/runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/EmittableComposer.kt
index fa7d7f5..6d646c7 100644
--- a/compose/compose-runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/EmittableComposer.kt
+++ b/compose/runtime/runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/EmittableComposer.kt
@@ -16,9 +16,9 @@
 package androidx.compose.runtime
 
 import android.view.View
-import android.widget.TextView
 import android.widget.Button
 import android.widget.LinearLayout
+import android.widget.TextView
 import androidx.compose.ui.viewinterop.emitView
 
 @Composable
diff --git a/compose/compose-runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/FlowAdapterTest.kt b/compose/runtime/runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/FlowAdapterTest.kt
similarity index 100%
rename from compose/compose-runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/FlowAdapterTest.kt
rename to compose/runtime/runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/FlowAdapterTest.kt
diff --git a/compose/compose-runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/NewCodeGenTests.kt b/compose/runtime/runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/NewCodeGenTests.kt
similarity index 100%
rename from compose/compose-runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/NewCodeGenTests.kt
rename to compose/runtime/runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/NewCodeGenTests.kt
diff --git a/compose/compose-runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/RecomposerTests.kt b/compose/runtime/runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/RecomposerTests.kt
similarity index 100%
rename from compose/compose-runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/RecomposerTests.kt
rename to compose/runtime/runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/RecomposerTests.kt
diff --git a/compose/compose-runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/RestartTests.kt b/compose/runtime/runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/RestartTests.kt
similarity index 100%
rename from compose/compose-runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/RestartTests.kt
rename to compose/runtime/runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/RestartTests.kt
diff --git a/compose/compose-runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/SuspendingEffectsTests.kt b/compose/runtime/runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/SuspendingEffectsTests.kt
similarity index 100%
rename from compose/compose-runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/SuspendingEffectsTests.kt
rename to compose/runtime/runtime/src/androidAndroidTest/kotlin/androidx/compose/runtime/SuspendingEffectsTests.kt
diff --git a/compose/compose-runtime/src/androidMain/AndroidManifest.xml b/compose/runtime/runtime/src/androidMain/AndroidManifest.xml
similarity index 100%
rename from compose/compose-runtime/src/androidMain/AndroidManifest.xml
rename to compose/runtime/runtime/src/androidMain/AndroidManifest.xml
diff --git a/compose/compose-runtime/src/androidMain/kotlin/androidx/compose/runtime/ActualAndroid.kt b/compose/runtime/runtime/src/androidMain/kotlin/androidx/compose/runtime/ActualAndroid.kt
similarity index 100%
rename from compose/compose-runtime/src/androidMain/kotlin/androidx/compose/runtime/ActualAndroid.kt
rename to compose/runtime/runtime/src/androidMain/kotlin/androidx/compose/runtime/ActualAndroid.kt
diff --git a/compose/compose-runtime/src/androidMain/kotlin/androidx/compose/runtime/KeySourceInfo.kt b/compose/runtime/runtime/src/androidMain/kotlin/androidx/compose/runtime/KeySourceInfo.kt
similarity index 100%
rename from compose/compose-runtime/src/androidMain/kotlin/androidx/compose/runtime/KeySourceInfo.kt
rename to compose/runtime/runtime/src/androidMain/kotlin/androidx/compose/runtime/KeySourceInfo.kt
diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/Ambient.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Ambient.kt
similarity index 100%
rename from compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/Ambient.kt
rename to compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Ambient.kt
diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/Applier.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Applier.kt
similarity index 100%
rename from compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/Applier.kt
rename to compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Applier.kt
diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/BitwiseOperators.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/BitwiseOperators.kt
similarity index 100%
rename from compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/BitwiseOperators.kt
rename to compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/BitwiseOperators.kt
diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/Composable.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composable.kt
similarity index 100%
rename from compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/Composable.kt
rename to compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composable.kt
diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/ComposableContract.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/ComposableContract.kt
similarity index 100%
rename from compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/ComposableContract.kt
rename to compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/ComposableContract.kt
diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/Compose.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Compose.kt
similarity index 100%
rename from compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/Compose.kt
rename to compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Compose.kt
diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/ComposeCompilerApi.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/ComposeCompilerApi.kt
similarity index 100%
rename from compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/ComposeCompilerApi.kt
rename to compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/ComposeCompilerApi.kt
diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/ComposeVersion.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/ComposeVersion.kt
similarity index 100%
rename from compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/ComposeVersion.kt
rename to compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/ComposeVersion.kt
diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/Composer.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composer.kt
similarity index 100%
rename from compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/Composer.kt
rename to compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composer.kt
diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/Composition.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composition.kt
similarity index 100%
rename from compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/Composition.kt
rename to compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composition.kt
diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/CompositionFrameClock.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/CompositionFrameClock.kt
similarity index 100%
rename from compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/CompositionFrameClock.kt
rename to compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/CompositionFrameClock.kt
diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/CompositionLifecycleObserver.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/CompositionLifecycleObserver.kt
similarity index 100%
rename from compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/CompositionLifecycleObserver.kt
rename to compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/CompositionLifecycleObserver.kt
diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/CompositionReference.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/CompositionReference.kt
similarity index 100%
rename from compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/CompositionReference.kt
rename to compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/CompositionReference.kt
diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/Effects.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Effects.kt
similarity index 100%
rename from compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/Effects.kt
rename to compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Effects.kt
diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/EmbeddingContext.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/EmbeddingContext.kt
similarity index 100%
rename from compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/EmbeddingContext.kt
rename to compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/EmbeddingContext.kt
diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/Emit.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Emit.kt
similarity index 100%
rename from compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/Emit.kt
rename to compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Emit.kt
diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/Expect.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Expect.kt
similarity index 100%
rename from compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/Expect.kt
rename to compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Expect.kt
diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/ExperimentalComposeApi.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/ExperimentalComposeApi.kt
similarity index 100%
rename from compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/ExperimentalComposeApi.kt
rename to compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/ExperimentalComposeApi.kt
diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/FlowAdapter.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/FlowAdapter.kt
similarity index 100%
rename from compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/FlowAdapter.kt
rename to compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/FlowAdapter.kt
diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/FrameManager.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/FrameManager.kt
similarity index 100%
rename from compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/FrameManager.kt
rename to compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/FrameManager.kt
diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/Immutable.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Immutable.kt
similarity index 100%
rename from compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/Immutable.kt
rename to compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Immutable.kt
diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/InternalComposeApi.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/InternalComposeApi.kt
similarity index 100%
rename from compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/InternalComposeApi.kt
rename to compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/InternalComposeApi.kt
diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/JoinedKey.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/JoinedKey.kt
similarity index 100%
rename from compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/JoinedKey.kt
rename to compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/JoinedKey.kt
diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/Key.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Key.kt
similarity index 100%
rename from compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/Key.kt
rename to compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Key.kt
diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/KeySourceInfo.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/KeySourceInfo.kt
similarity index 100%
rename from compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/KeySourceInfo.kt
rename to compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/KeySourceInfo.kt
diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/Latch.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Latch.kt
similarity index 100%
rename from compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/Latch.kt
rename to compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Latch.kt
diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/MutableState.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/MutableState.kt
similarity index 100%
rename from compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/MutableState.kt
rename to compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/MutableState.kt
diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/NoLiveLiterals.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/NoLiveLiterals.kt
similarity index 100%
rename from compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/NoLiveLiterals.kt
rename to compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/NoLiveLiterals.kt
diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/ObserverMap.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/ObserverMap.kt
similarity index 100%
rename from compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/ObserverMap.kt
rename to compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/ObserverMap.kt
diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/OpaqueKey.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/OpaqueKey.kt
similarity index 100%
rename from compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/OpaqueKey.kt
rename to compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/OpaqueKey.kt
diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/Recomposer.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Recomposer.kt
similarity index 100%
rename from compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/Recomposer.kt
rename to compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Recomposer.kt
diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/Remember.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Remember.kt
similarity index 100%
rename from compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/Remember.kt
rename to compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Remember.kt
diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/SlotTable.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SlotTable.kt
similarity index 100%
rename from compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/SlotTable.kt
rename to compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SlotTable.kt
diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/Stable.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Stable.kt
similarity index 100%
rename from compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/Stable.kt
rename to compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Stable.kt
diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/StableMarker.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/StableMarker.kt
similarity index 100%
rename from compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/StableMarker.kt
rename to compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/StableMarker.kt
diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/Stack.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Stack.kt
similarity index 100%
rename from compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/Stack.kt
rename to compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Stack.kt
diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/SuspendingEffects.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SuspendingEffects.kt
similarity index 100%
rename from compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/SuspendingEffects.kt
rename to compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SuspendingEffects.kt
diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/Trace.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Trace.kt
similarity index 100%
rename from compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/Trace.kt
rename to compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Trace.kt
diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/UnionType.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/UnionType.kt
similarity index 100%
rename from compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/UnionType.kt
rename to compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/UnionType.kt
diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/ValueHolders.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/ValueHolders.kt
similarity index 100%
rename from compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/ValueHolders.kt
rename to compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/ValueHolders.kt
diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/collection/ExperimentalCollectionApi.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/collection/ExperimentalCollectionApi.kt
similarity index 100%
rename from compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/collection/ExperimentalCollectionApi.kt
rename to compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/collection/ExperimentalCollectionApi.kt
diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/collection/MutableVector.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/collection/MutableVector.kt
similarity index 100%
rename from compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/collection/MutableVector.kt
rename to compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/collection/MutableVector.kt
diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/frames/FrameContainers.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/frames/FrameContainers.kt
similarity index 100%
rename from compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/frames/FrameContainers.kt
rename to compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/frames/FrameContainers.kt
diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/frames/FrameIdSet.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/frames/FrameIdSet.kt
similarity index 100%
rename from compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/frames/FrameIdSet.kt
rename to compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/frames/FrameIdSet.kt
diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/frames/Frames.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/frames/Frames.kt
similarity index 100%
rename from compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/frames/Frames.kt
rename to compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/frames/Frames.kt
diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/internal/ComposableLambda.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/internal/ComposableLambda.kt
similarity index 100%
rename from compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/internal/ComposableLambda.kt
rename to compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/internal/ComposableLambda.kt
diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/internal/LiveLiteral.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/internal/LiveLiteral.kt
similarity index 100%
rename from compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/internal/LiveLiteral.kt
rename to compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/internal/LiveLiteral.kt
diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/ListUtils.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/ListUtils.kt
similarity index 100%
rename from compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/ListUtils.kt
rename to compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/ListUtils.kt
diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/Snapshot.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/Snapshot.kt
similarity index 98%
rename from compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/Snapshot.kt
rename to compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/Snapshot.kt
index 0785ddc..0655c7a 100644
--- a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/Snapshot.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/Snapshot.kt
@@ -1371,6 +1371,15 @@
     openSnapshots = openSnapshots.set(it.id)
 }
 
+// A value to use to initialize the snapshot local variable of writable below. The value of this
+// doesn't matter as it is just used to initialize the local that is immediately overwritten by
+// Snapshot.current. This is done to avoid a compiler error complaining that the var has not been
+// initialized. This can be removed once contracts are out of experimental; then we can mark sync
+// with the correct contracts so the compiler would be able to figure out that the variable is
+// initialized.
+@PublishedApi
+internal val snapshotInitializer: Snapshot = currentGlobalSnapshot
+
 private fun <T> takeNewGlobalSnapshot(
     previousGlobalSnapshot: Snapshot,
     block: (invalid: SnapshotIdSet) -> T
@@ -1602,8 +1611,15 @@
  * called for the first state record in a state object. A record is writable if it was created in
  * the current mutable snapshot.
  */
-inline fun <T : StateRecord, R> T.writable(state: StateObject, block: T.() -> R): R =
-    this.writable(state, Snapshot.current, block)
+inline fun <T : StateRecord, R> T.writable(state: StateObject, block: T.() -> R): R {
+    var snapshot: Snapshot = snapshotInitializer
+    return sync {
+        snapshot = Snapshot.current
+        this.writableRecord(state, snapshot).block()
+    }.also {
+        notifyWrite(snapshot, state)
+    }
+}
 
 /**
  * Produce a set of optimistic merges of the state records, this is performed outside the
diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/SnapshotIdSet.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/SnapshotIdSet.kt
similarity index 100%
rename from compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/SnapshotIdSet.kt
rename to compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/SnapshotIdSet.kt
diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/SnapshotStateList.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/SnapshotStateList.kt
similarity index 100%
rename from compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/SnapshotStateList.kt
rename to compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/SnapshotStateList.kt
diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/SnapshotStateMap.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/SnapshotStateMap.kt
similarity index 100%
rename from compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/SnapshotStateMap.kt
rename to compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/SnapshotStateMap.kt
diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/SnapshotStateObserver.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/SnapshotStateObserver.kt
similarity index 100%
rename from compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/SnapshotStateObserver.kt
rename to compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/SnapshotStateObserver.kt
diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/tooling/InspectionTables.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/tooling/InspectionTables.kt
similarity index 100%
rename from compose/compose-runtime/src/commonMain/kotlin/androidx/compose/runtime/tooling/InspectionTables.kt
rename to compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/tooling/InspectionTables.kt
diff --git a/compose/compose-runtime/src/desktopMain/kotlin/androidx/compose/runtime/ActualDesktop.kt b/compose/runtime/runtime/src/desktopMain/kotlin/androidx/compose/runtime/ActualDesktop.kt
similarity index 99%
rename from compose/compose-runtime/src/desktopMain/kotlin/androidx/compose/runtime/ActualDesktop.kt
rename to compose/runtime/runtime/src/desktopMain/kotlin/androidx/compose/runtime/ActualDesktop.kt
index 1ae7a9b..8806b4b 100644
--- a/compose/compose-runtime/src/desktopMain/kotlin/androidx/compose/runtime/ActualDesktop.kt
+++ b/compose/runtime/runtime/src/desktopMain/kotlin/androidx/compose/runtime/ActualDesktop.kt
@@ -16,9 +16,9 @@
 
 package androidx.compose.runtime
 
+import androidx.compose.runtime.dispatch.DesktopUiDispatcher
 import javax.swing.SwingUtilities
 import kotlin.coroutines.CoroutineContext
-import androidx.compose.runtime.dispatch.DesktopUiDispatcher
 
 // API to allow override embedding context creation mechanism for tests.
 var EmbeddingContextFactory: (() -> EmbeddingContext)? = null
diff --git a/compose/compose-runtime/src/jvmMain/kotlin/androidx/compose/runtime/ActualJvm.kt b/compose/runtime/runtime/src/jvmMain/kotlin/androidx/compose/runtime/ActualJvm.kt
similarity index 99%
rename from compose/compose-runtime/src/jvmMain/kotlin/androidx/compose/runtime/ActualJvm.kt
rename to compose/runtime/runtime/src/jvmMain/kotlin/androidx/compose/runtime/ActualJvm.kt
index 88a0946c..86f1cba 100644
--- a/compose/compose-runtime/src/jvmMain/kotlin/androidx/compose/runtime/ActualJvm.kt
+++ b/compose/runtime/runtime/src/jvmMain/kotlin/androidx/compose/runtime/ActualJvm.kt
@@ -16,9 +16,9 @@
 
 package androidx.compose.runtime
 
+import kotlinx.collections.immutable.PersistentList
 import kotlinx.collections.immutable.PersistentMap
 import kotlinx.collections.immutable.persistentHashMapOf
-import kotlinx.collections.immutable.PersistentList
 import kotlinx.collections.immutable.persistentListOf
 
 internal actual typealias BitSet = java.util.BitSet
diff --git a/compose/compose-runtime/src/jvmMain/kotlin/androidx/compose/runtime/internal/ComposableLambdaN.kt b/compose/runtime/runtime/src/jvmMain/kotlin/androidx/compose/runtime/internal/ComposableLambdaN.kt
similarity index 100%
rename from compose/compose-runtime/src/jvmMain/kotlin/androidx/compose/runtime/internal/ComposableLambdaN.kt
rename to compose/runtime/runtime/src/jvmMain/kotlin/androidx/compose/runtime/internal/ComposableLambdaN.kt
diff --git a/compose/compose-runtime/src/test/kotlin/androidx/compose/runtime/CompositionTests.kt b/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/CompositionTests.kt
similarity index 100%
rename from compose/compose-runtime/src/test/kotlin/androidx/compose/runtime/CompositionTests.kt
rename to compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/CompositionTests.kt
diff --git a/compose/compose-runtime/src/test/kotlin/androidx/compose/runtime/LatchTest.kt b/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/LatchTest.kt
similarity index 100%
rename from compose/compose-runtime/src/test/kotlin/androidx/compose/runtime/LatchTest.kt
rename to compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/LatchTest.kt
diff --git a/compose/compose-runtime/src/test/kotlin/androidx/compose/runtime/ObserverMapTests.kt b/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/ObserverMapTests.kt
similarity index 99%
rename from compose/compose-runtime/src/test/kotlin/androidx/compose/runtime/ObserverMapTests.kt
rename to compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/ObserverMapTests.kt
index fdbf94f..8828ff6 100644
--- a/compose/compose-runtime/src/test/kotlin/androidx/compose/runtime/ObserverMapTests.kt
+++ b/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/ObserverMapTests.kt
@@ -16,9 +16,9 @@
 
 package androidx.compose.runtime
 
-import kotlin.test.assertEquals
 import kotlin.test.BeforeTest
 import kotlin.test.Test
+import kotlin.test.assertEquals
 
 class ObserverMapTests {
 
diff --git a/compose/compose-runtime/src/test/kotlin/androidx/compose/runtime/SlotTableTests.kt b/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/SlotTableTests.kt
similarity index 100%
rename from compose/compose-runtime/src/test/kotlin/androidx/compose/runtime/SlotTableTests.kt
rename to compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/SlotTableTests.kt
diff --git a/compose/compose-runtime/src/test/kotlin/androidx/compose/runtime/collection/MutableVectorTest.kt b/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/collection/MutableVectorTest.kt
similarity index 100%
rename from compose/compose-runtime/src/test/kotlin/androidx/compose/runtime/collection/MutableVectorTest.kt
rename to compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/collection/MutableVectorTest.kt
diff --git a/compose/compose-runtime/src/test/kotlin/androidx/compose/runtime/mock/ComposeContact.kt b/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/mock/ComposeContact.kt
similarity index 100%
rename from compose/compose-runtime/src/test/kotlin/androidx/compose/runtime/mock/ComposeContact.kt
rename to compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/mock/ComposeContact.kt
diff --git a/compose/compose-runtime/src/test/kotlin/androidx/compose/runtime/mock/ComposePoints.kt b/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/mock/ComposePoints.kt
similarity index 100%
rename from compose/compose-runtime/src/test/kotlin/androidx/compose/runtime/mock/ComposePoints.kt
rename to compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/mock/ComposePoints.kt
diff --git a/compose/compose-runtime/src/test/kotlin/androidx/compose/runtime/mock/ComposeReport.kt b/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/mock/ComposeReport.kt
similarity index 100%
rename from compose/compose-runtime/src/test/kotlin/androidx/compose/runtime/mock/ComposeReport.kt
rename to compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/mock/ComposeReport.kt
diff --git a/compose/compose-runtime/src/test/kotlin/androidx/compose/runtime/mock/Contact.kt b/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/mock/Contact.kt
similarity index 100%
rename from compose/compose-runtime/src/test/kotlin/androidx/compose/runtime/mock/Contact.kt
rename to compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/mock/Contact.kt
diff --git a/compose/compose-runtime/src/test/kotlin/androidx/compose/runtime/mock/ContactModel.kt b/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/mock/ContactModel.kt
similarity index 100%
rename from compose/compose-runtime/src/test/kotlin/androidx/compose/runtime/mock/ContactModel.kt
rename to compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/mock/ContactModel.kt
diff --git a/compose/compose-runtime/src/test/kotlin/androidx/compose/runtime/mock/MockViewValidator.kt b/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/mock/MockViewValidator.kt
similarity index 100%
rename from compose/compose-runtime/src/test/kotlin/androidx/compose/runtime/mock/MockViewValidator.kt
rename to compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/mock/MockViewValidator.kt
diff --git a/compose/compose-runtime/src/test/kotlin/androidx/compose/runtime/mock/Point.kt b/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/mock/Point.kt
similarity index 100%
rename from compose/compose-runtime/src/test/kotlin/androidx/compose/runtime/mock/Point.kt
rename to compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/mock/Point.kt
diff --git a/compose/compose-runtime/src/test/kotlin/androidx/compose/runtime/mock/Report.kt b/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/mock/Report.kt
similarity index 100%
rename from compose/compose-runtime/src/test/kotlin/androidx/compose/runtime/mock/Report.kt
rename to compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/mock/Report.kt
diff --git a/compose/compose-runtime/src/test/kotlin/androidx/compose/runtime/mock/View.kt b/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/mock/View.kt
similarity index 100%
rename from compose/compose-runtime/src/test/kotlin/androidx/compose/runtime/mock/View.kt
rename to compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/mock/View.kt
diff --git a/compose/compose-runtime/src/test/kotlin/androidx/compose/runtime/mock/ViewApplier.kt b/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/mock/ViewApplier.kt
similarity index 100%
rename from compose/compose-runtime/src/test/kotlin/androidx/compose/runtime/mock/ViewApplier.kt
rename to compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/mock/ViewApplier.kt
diff --git a/compose/compose-runtime/src/test/kotlin/androidx/compose/runtime/mock/Views.kt b/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/mock/Views.kt
similarity index 100%
rename from compose/compose-runtime/src/test/kotlin/androidx/compose/runtime/mock/Views.kt
rename to compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/mock/Views.kt
diff --git a/compose/compose-runtime/src/test/kotlin/androidx/compose/runtime/snapshots/SnapshotIdSetTests.kt b/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/snapshots/SnapshotIdSetTests.kt
similarity index 100%
rename from compose/compose-runtime/src/test/kotlin/androidx/compose/runtime/snapshots/SnapshotIdSetTests.kt
rename to compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/snapshots/SnapshotIdSetTests.kt
diff --git a/compose/compose-runtime/src/test/kotlin/androidx/compose/runtime/snapshots/SnapshotStateListTests.kt b/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/snapshots/SnapshotStateListTests.kt
similarity index 99%
rename from compose/compose-runtime/src/test/kotlin/androidx/compose/runtime/snapshots/SnapshotStateListTests.kt
rename to compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/snapshots/SnapshotStateListTests.kt
index 52e4a71..8f0e0f1 100644
--- a/compose/compose-runtime/src/test/kotlin/androidx/compose/runtime/snapshots/SnapshotStateListTests.kt
+++ b/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/snapshots/SnapshotStateListTests.kt
@@ -16,13 +16,12 @@
 
 package androidx.compose.runtime.snapshots
 
-import java.lang.IllegalStateException
+import androidx.compose.runtime.ExperimentalComposeApi
+import androidx.compose.runtime.mutableStateListOf
 import kotlin.test.Test
 import kotlin.test.assertEquals
 import kotlin.test.assertFalse
 import kotlin.test.assertTrue
-import androidx.compose.runtime.ExperimentalComposeApi
-import androidx.compose.runtime.mutableStateListOf
 
 class SnapshotStateListTests {
     @Test
diff --git a/compose/compose-runtime/src/test/kotlin/androidx/compose/runtime/snapshots/SnapshotStateMapTests.kt b/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/snapshots/SnapshotStateMapTests.kt
similarity index 100%
rename from compose/compose-runtime/src/test/kotlin/androidx/compose/runtime/snapshots/SnapshotStateMapTests.kt
rename to compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/snapshots/SnapshotStateMapTests.kt
diff --git a/compose/compose-runtime/src/test/kotlin/androidx/compose/runtime/snapshots/SnapshotStateObserverTests.kt b/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/snapshots/SnapshotStateObserverTests.kt
similarity index 100%
rename from compose/compose-runtime/src/test/kotlin/androidx/compose/runtime/snapshots/SnapshotStateObserverTests.kt
rename to compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/snapshots/SnapshotStateObserverTests.kt
diff --git a/compose/compose-runtime/src/test/kotlin/androidx/compose/runtime/snapshots/SnapshotTests.kt b/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/snapshots/SnapshotTests.kt
similarity index 100%
rename from compose/compose-runtime/src/test/kotlin/androidx/compose/runtime/snapshots/SnapshotTests.kt
rename to compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/snapshots/SnapshotTests.kt
diff --git a/datastore/datastore-core/api/current.txt b/datastore/datastore-core/api/current.txt
index c29edba..80f8b50 100644
--- a/datastore/datastore-core/api/current.txt
+++ b/datastore/datastore-core/api/current.txt
@@ -23,8 +23,8 @@
 
   public final class DataStoreFactory {
     ctor public DataStoreFactory();
-    method public <T> androidx.datastore.DataStore<T> create(kotlin.jvm.functions.Function0<? extends java.io.File> produceFile, androidx.datastore.Serializer<T> serializer, androidx.datastore.handlers.ReplaceFileCorruptionHandler<T>? corruptionHandler = null, java.util.List<? extends kotlin.jvm.functions.Function0<? extends androidx.datastore.DataMigration<T>>> migrationProducers = listOf(), kotlinx.coroutines.CoroutineScope scope = CoroutineScope(Dispatchers.IO + SupervisorJob()));
-    method public <T> androidx.datastore.DataStore<T> create(kotlin.jvm.functions.Function0<? extends java.io.File> produceFile, androidx.datastore.Serializer<T> serializer, androidx.datastore.handlers.ReplaceFileCorruptionHandler<T>? corruptionHandler = null, java.util.List<? extends kotlin.jvm.functions.Function0<? extends androidx.datastore.DataMigration<T>>> migrationProducers = listOf());
+    method public <T> androidx.datastore.DataStore<T> create(kotlin.jvm.functions.Function0<? extends java.io.File> produceFile, androidx.datastore.Serializer<T> serializer, androidx.datastore.handlers.ReplaceFileCorruptionHandler<T>? corruptionHandler = null, java.util.List<? extends androidx.datastore.DataMigration<T>> migrations = listOf(), kotlinx.coroutines.CoroutineScope scope = CoroutineScope(Dispatchers.IO + SupervisorJob()));
+    method public <T> androidx.datastore.DataStore<T> create(kotlin.jvm.functions.Function0<? extends java.io.File> produceFile, androidx.datastore.Serializer<T> serializer, androidx.datastore.handlers.ReplaceFileCorruptionHandler<T>? corruptionHandler = null, java.util.List<? extends androidx.datastore.DataMigration<T>> migrations = listOf());
     method public <T> androidx.datastore.DataStore<T> create(kotlin.jvm.functions.Function0<? extends java.io.File> produceFile, androidx.datastore.Serializer<T> serializer, androidx.datastore.handlers.ReplaceFileCorruptionHandler<T>? corruptionHandler = null);
     method public <T> androidx.datastore.DataStore<T> create(kotlin.jvm.functions.Function0<? extends java.io.File> produceFile, androidx.datastore.Serializer<T> serializer);
   }
@@ -47,13 +47,17 @@
 
 package androidx.datastore.migrations {
 
-  public interface MigrationFromSharedPreferences<T> {
-    method public suspend Object? migrate(androidx.datastore.migrations.SharedPreferencesView prefs, T? currentData, kotlin.coroutines.Continuation<? super T> p);
-    method public default suspend Object? shouldMigrate(T? currentData, kotlin.coroutines.Continuation<? super java.lang.Boolean> p);
+  public final class SharedPreferencesMigration<T> implements androidx.datastore.DataMigration<T> {
+    ctor public SharedPreferencesMigration(android.content.Context context, String sharedPreferencesName, java.util.Set<java.lang.String>? keysToMigrate, boolean deleteEmptyPreferences, kotlin.jvm.functions.Function2<? super T,? super kotlin.coroutines.Continuation<? super java.lang.Boolean>,?> shouldRunMigration, kotlin.jvm.functions.Function3<? super androidx.datastore.migrations.SharedPreferencesView,? super T,? super kotlin.coroutines.Continuation<? super T>,?> migrate);
+    ctor public SharedPreferencesMigration(android.content.Context context, String sharedPreferencesName, java.util.Set<java.lang.String>? keysToMigrate, boolean deleteEmptyPreferences, kotlin.jvm.functions.Function3<? super androidx.datastore.migrations.SharedPreferencesView,? super T,? super kotlin.coroutines.Continuation<? super T>,?> migrate);
+    ctor public SharedPreferencesMigration(android.content.Context context, String sharedPreferencesName, java.util.Set<java.lang.String>? keysToMigrate, kotlin.jvm.functions.Function3<? super androidx.datastore.migrations.SharedPreferencesView,? super T,? super kotlin.coroutines.Continuation<? super T>,?> migrate);
+    ctor public SharedPreferencesMigration(android.content.Context context, String sharedPreferencesName, kotlin.jvm.functions.Function3<? super androidx.datastore.migrations.SharedPreferencesView,? super T,? super kotlin.coroutines.Continuation<? super T>,?> migrate);
+    method @kotlin.jvm.Throws(exceptionClasses=IOException::class) public suspend Object? cleanUp(kotlin.coroutines.Continuation<? super kotlin.Unit> p) throws java.io.IOException;
+    method public suspend Object? migrate(T? currentData, kotlin.coroutines.Continuation<? super T> p);
+    method public suspend Object? shouldMigrate(T? currentData, kotlin.coroutines.Continuation<? super java.lang.Boolean> p);
   }
 
-  public final class SharedPreferencesMigration {
-    method public static <T> kotlin.jvm.functions.Function0<androidx.datastore.DataMigration<T>> SharedPreferencesMigration(android.content.Context context, String sharedPreferencesName, androidx.datastore.migrations.MigrationFromSharedPreferences<T> migration, java.util.Set<java.lang.String>? keysToMigrate = MIGRATE_ALL_KEYS, boolean deleteEmptyPreferences = true);
+  public final class SharedPreferencesMigrationKt {
   }
 
   public final class SharedPreferencesView {
diff --git a/datastore/datastore-core/api/public_plus_experimental_current.txt b/datastore/datastore-core/api/public_plus_experimental_current.txt
index c29edba..80f8b50 100644
--- a/datastore/datastore-core/api/public_plus_experimental_current.txt
+++ b/datastore/datastore-core/api/public_plus_experimental_current.txt
@@ -23,8 +23,8 @@
 
   public final class DataStoreFactory {
     ctor public DataStoreFactory();
-    method public <T> androidx.datastore.DataStore<T> create(kotlin.jvm.functions.Function0<? extends java.io.File> produceFile, androidx.datastore.Serializer<T> serializer, androidx.datastore.handlers.ReplaceFileCorruptionHandler<T>? corruptionHandler = null, java.util.List<? extends kotlin.jvm.functions.Function0<? extends androidx.datastore.DataMigration<T>>> migrationProducers = listOf(), kotlinx.coroutines.CoroutineScope scope = CoroutineScope(Dispatchers.IO + SupervisorJob()));
-    method public <T> androidx.datastore.DataStore<T> create(kotlin.jvm.functions.Function0<? extends java.io.File> produceFile, androidx.datastore.Serializer<T> serializer, androidx.datastore.handlers.ReplaceFileCorruptionHandler<T>? corruptionHandler = null, java.util.List<? extends kotlin.jvm.functions.Function0<? extends androidx.datastore.DataMigration<T>>> migrationProducers = listOf());
+    method public <T> androidx.datastore.DataStore<T> create(kotlin.jvm.functions.Function0<? extends java.io.File> produceFile, androidx.datastore.Serializer<T> serializer, androidx.datastore.handlers.ReplaceFileCorruptionHandler<T>? corruptionHandler = null, java.util.List<? extends androidx.datastore.DataMigration<T>> migrations = listOf(), kotlinx.coroutines.CoroutineScope scope = CoroutineScope(Dispatchers.IO + SupervisorJob()));
+    method public <T> androidx.datastore.DataStore<T> create(kotlin.jvm.functions.Function0<? extends java.io.File> produceFile, androidx.datastore.Serializer<T> serializer, androidx.datastore.handlers.ReplaceFileCorruptionHandler<T>? corruptionHandler = null, java.util.List<? extends androidx.datastore.DataMigration<T>> migrations = listOf());
     method public <T> androidx.datastore.DataStore<T> create(kotlin.jvm.functions.Function0<? extends java.io.File> produceFile, androidx.datastore.Serializer<T> serializer, androidx.datastore.handlers.ReplaceFileCorruptionHandler<T>? corruptionHandler = null);
     method public <T> androidx.datastore.DataStore<T> create(kotlin.jvm.functions.Function0<? extends java.io.File> produceFile, androidx.datastore.Serializer<T> serializer);
   }
@@ -47,13 +47,17 @@
 
 package androidx.datastore.migrations {
 
-  public interface MigrationFromSharedPreferences<T> {
-    method public suspend Object? migrate(androidx.datastore.migrations.SharedPreferencesView prefs, T? currentData, kotlin.coroutines.Continuation<? super T> p);
-    method public default suspend Object? shouldMigrate(T? currentData, kotlin.coroutines.Continuation<? super java.lang.Boolean> p);
+  public final class SharedPreferencesMigration<T> implements androidx.datastore.DataMigration<T> {
+    ctor public SharedPreferencesMigration(android.content.Context context, String sharedPreferencesName, java.util.Set<java.lang.String>? keysToMigrate, boolean deleteEmptyPreferences, kotlin.jvm.functions.Function2<? super T,? super kotlin.coroutines.Continuation<? super java.lang.Boolean>,?> shouldRunMigration, kotlin.jvm.functions.Function3<? super androidx.datastore.migrations.SharedPreferencesView,? super T,? super kotlin.coroutines.Continuation<? super T>,?> migrate);
+    ctor public SharedPreferencesMigration(android.content.Context context, String sharedPreferencesName, java.util.Set<java.lang.String>? keysToMigrate, boolean deleteEmptyPreferences, kotlin.jvm.functions.Function3<? super androidx.datastore.migrations.SharedPreferencesView,? super T,? super kotlin.coroutines.Continuation<? super T>,?> migrate);
+    ctor public SharedPreferencesMigration(android.content.Context context, String sharedPreferencesName, java.util.Set<java.lang.String>? keysToMigrate, kotlin.jvm.functions.Function3<? super androidx.datastore.migrations.SharedPreferencesView,? super T,? super kotlin.coroutines.Continuation<? super T>,?> migrate);
+    ctor public SharedPreferencesMigration(android.content.Context context, String sharedPreferencesName, kotlin.jvm.functions.Function3<? super androidx.datastore.migrations.SharedPreferencesView,? super T,? super kotlin.coroutines.Continuation<? super T>,?> migrate);
+    method @kotlin.jvm.Throws(exceptionClasses=IOException::class) public suspend Object? cleanUp(kotlin.coroutines.Continuation<? super kotlin.Unit> p) throws java.io.IOException;
+    method public suspend Object? migrate(T? currentData, kotlin.coroutines.Continuation<? super T> p);
+    method public suspend Object? shouldMigrate(T? currentData, kotlin.coroutines.Continuation<? super java.lang.Boolean> p);
   }
 
-  public final class SharedPreferencesMigration {
-    method public static <T> kotlin.jvm.functions.Function0<androidx.datastore.DataMigration<T>> SharedPreferencesMigration(android.content.Context context, String sharedPreferencesName, androidx.datastore.migrations.MigrationFromSharedPreferences<T> migration, java.util.Set<java.lang.String>? keysToMigrate = MIGRATE_ALL_KEYS, boolean deleteEmptyPreferences = true);
+  public final class SharedPreferencesMigrationKt {
   }
 
   public final class SharedPreferencesView {
diff --git a/datastore/datastore-core/api/restricted_current.txt b/datastore/datastore-core/api/restricted_current.txt
index c29edba..80f8b50 100644
--- a/datastore/datastore-core/api/restricted_current.txt
+++ b/datastore/datastore-core/api/restricted_current.txt
@@ -23,8 +23,8 @@
 
   public final class DataStoreFactory {
     ctor public DataStoreFactory();
-    method public <T> androidx.datastore.DataStore<T> create(kotlin.jvm.functions.Function0<? extends java.io.File> produceFile, androidx.datastore.Serializer<T> serializer, androidx.datastore.handlers.ReplaceFileCorruptionHandler<T>? corruptionHandler = null, java.util.List<? extends kotlin.jvm.functions.Function0<? extends androidx.datastore.DataMigration<T>>> migrationProducers = listOf(), kotlinx.coroutines.CoroutineScope scope = CoroutineScope(Dispatchers.IO + SupervisorJob()));
-    method public <T> androidx.datastore.DataStore<T> create(kotlin.jvm.functions.Function0<? extends java.io.File> produceFile, androidx.datastore.Serializer<T> serializer, androidx.datastore.handlers.ReplaceFileCorruptionHandler<T>? corruptionHandler = null, java.util.List<? extends kotlin.jvm.functions.Function0<? extends androidx.datastore.DataMigration<T>>> migrationProducers = listOf());
+    method public <T> androidx.datastore.DataStore<T> create(kotlin.jvm.functions.Function0<? extends java.io.File> produceFile, androidx.datastore.Serializer<T> serializer, androidx.datastore.handlers.ReplaceFileCorruptionHandler<T>? corruptionHandler = null, java.util.List<? extends androidx.datastore.DataMigration<T>> migrations = listOf(), kotlinx.coroutines.CoroutineScope scope = CoroutineScope(Dispatchers.IO + SupervisorJob()));
+    method public <T> androidx.datastore.DataStore<T> create(kotlin.jvm.functions.Function0<? extends java.io.File> produceFile, androidx.datastore.Serializer<T> serializer, androidx.datastore.handlers.ReplaceFileCorruptionHandler<T>? corruptionHandler = null, java.util.List<? extends androidx.datastore.DataMigration<T>> migrations = listOf());
     method public <T> androidx.datastore.DataStore<T> create(kotlin.jvm.functions.Function0<? extends java.io.File> produceFile, androidx.datastore.Serializer<T> serializer, androidx.datastore.handlers.ReplaceFileCorruptionHandler<T>? corruptionHandler = null);
     method public <T> androidx.datastore.DataStore<T> create(kotlin.jvm.functions.Function0<? extends java.io.File> produceFile, androidx.datastore.Serializer<T> serializer);
   }
@@ -47,13 +47,17 @@
 
 package androidx.datastore.migrations {
 
-  public interface MigrationFromSharedPreferences<T> {
-    method public suspend Object? migrate(androidx.datastore.migrations.SharedPreferencesView prefs, T? currentData, kotlin.coroutines.Continuation<? super T> p);
-    method public default suspend Object? shouldMigrate(T? currentData, kotlin.coroutines.Continuation<? super java.lang.Boolean> p);
+  public final class SharedPreferencesMigration<T> implements androidx.datastore.DataMigration<T> {
+    ctor public SharedPreferencesMigration(android.content.Context context, String sharedPreferencesName, java.util.Set<java.lang.String>? keysToMigrate, boolean deleteEmptyPreferences, kotlin.jvm.functions.Function2<? super T,? super kotlin.coroutines.Continuation<? super java.lang.Boolean>,?> shouldRunMigration, kotlin.jvm.functions.Function3<? super androidx.datastore.migrations.SharedPreferencesView,? super T,? super kotlin.coroutines.Continuation<? super T>,?> migrate);
+    ctor public SharedPreferencesMigration(android.content.Context context, String sharedPreferencesName, java.util.Set<java.lang.String>? keysToMigrate, boolean deleteEmptyPreferences, kotlin.jvm.functions.Function3<? super androidx.datastore.migrations.SharedPreferencesView,? super T,? super kotlin.coroutines.Continuation<? super T>,?> migrate);
+    ctor public SharedPreferencesMigration(android.content.Context context, String sharedPreferencesName, java.util.Set<java.lang.String>? keysToMigrate, kotlin.jvm.functions.Function3<? super androidx.datastore.migrations.SharedPreferencesView,? super T,? super kotlin.coroutines.Continuation<? super T>,?> migrate);
+    ctor public SharedPreferencesMigration(android.content.Context context, String sharedPreferencesName, kotlin.jvm.functions.Function3<? super androidx.datastore.migrations.SharedPreferencesView,? super T,? super kotlin.coroutines.Continuation<? super T>,?> migrate);
+    method @kotlin.jvm.Throws(exceptionClasses=IOException::class) public suspend Object? cleanUp(kotlin.coroutines.Continuation<? super kotlin.Unit> p) throws java.io.IOException;
+    method public suspend Object? migrate(T? currentData, kotlin.coroutines.Continuation<? super T> p);
+    method public suspend Object? shouldMigrate(T? currentData, kotlin.coroutines.Continuation<? super java.lang.Boolean> p);
   }
 
-  public final class SharedPreferencesMigration {
-    method public static <T> kotlin.jvm.functions.Function0<androidx.datastore.DataMigration<T>> SharedPreferencesMigration(android.content.Context context, String sharedPreferencesName, androidx.datastore.migrations.MigrationFromSharedPreferences<T> migration, java.util.Set<java.lang.String>? keysToMigrate = MIGRATE_ALL_KEYS, boolean deleteEmptyPreferences = true);
+  public final class SharedPreferencesMigrationKt {
   }
 
   public final class SharedPreferencesView {
diff --git a/datastore/datastore-core/src/androidTest/java/migrations/SharedPreferencesMigrationTest.kt b/datastore/datastore-core/src/androidTest/java/migrations/SharedPreferencesMigrationTest.kt
index b0edfb0..bb90413 100644
--- a/datastore/datastore-core/src/androidTest/java/migrations/SharedPreferencesMigrationTest.kt
+++ b/datastore/datastore-core/src/androidTest/java/migrations/SharedPreferencesMigrationTest.kt
@@ -58,20 +58,12 @@
 
     @Test
     fun testShouldMigrateSkipsMigration() = runBlockingTest {
-        val migration = object : MigrationFromSharedPreferences<Byte> {
-            override suspend fun shouldMigrate(currentData: Byte) = false
-
-            override suspend fun migrate(
-                prefs: SharedPreferencesView,
-                currentData: Byte
-            ) = throw IllegalStateException("Migration is skipped.")
-        }
-
-        val sharedPrefsMigration = SharedPreferencesMigration(
+        val sharedPrefsMigration = SharedPreferencesMigration<Byte>(
             context = context,
             sharedPreferencesName = sharedPrefsName,
-            migration = migration
-        )
+            shouldRunMigration = { false }) { _: SharedPreferencesView, _: Byte ->
+            throw IllegalStateException("Migration should've been skipped.")
+        }
 
         val dataStore = getDataStoreWithMigrations(listOf(sharedPrefsMigration))
 
@@ -91,28 +83,17 @@
                 .putInt(notMigratedKey, 123).commit()
         ).isTrue()
 
-        val migration = object : MigrationFromSharedPreferences<Byte> {
-            override suspend fun shouldMigrate(currentData: Byte) = true
-
-            override suspend fun migrate(
-                prefs: SharedPreferencesView,
-                currentData: Byte
-            ): Byte {
-                assertThat(prefs.getInt(includedKey, -1)).isEqualTo(includedVal)
-                assertThrows<IllegalStateException> { prefs.getInt(notMigratedKey, -1) }
-
-                assertThat(prefs.getAll()).isEqualTo(mapOf(includedKey to includedVal))
-
-                return 99.toByte()
-            }
-        }
-
         val sharedPrefsMigration = SharedPreferencesMigration(
             context = context,
             sharedPreferencesName = sharedPrefsName,
-            migration = migration,
             keysToMigrate = setOf(includedKey)
-        )
+        ) { prefs: SharedPreferencesView, _: Byte ->
+            assertThat(prefs.getInt(includedKey, -1)).isEqualTo(includedVal)
+            assertThrows<IllegalStateException> { prefs.getInt(notMigratedKey, -1) }
+            assertThat(prefs.getAll()).isEqualTo(mapOf(includedKey to includedVal))
+
+            99.toByte()
+        }
 
         val dataStore = getDataStoreWithMigrations(listOf(sharedPrefsMigration))
 
@@ -135,27 +116,17 @@
                 .commit()
         ).isTrue()
 
-        val migration = object : MigrationFromSharedPreferences<Byte> {
-            override suspend fun shouldMigrate(currentData: Byte) = true
-
-            override suspend fun migrate(
-                prefs: SharedPreferencesView,
-                currentData: Byte
-            ): Byte {
-                assertThat(prefs.getInt(key1, -1)).isEqualTo(val1)
-                assertThat(prefs.getInt(key2, -1)).isEqualTo(val2)
-
-                assertThat(prefs.getAll()).isEqualTo(mapOf(key1 to val1, key2 to val2))
-
-                return 99.toByte()
-            }
-        }
-
         val sharedPrefsMigration = SharedPreferencesMigration(
             context = context,
-            sharedPreferencesName = sharedPrefsName,
-            migration = migration
-        )
+            sharedPreferencesName = sharedPrefsName
+        ) { prefs: SharedPreferencesView, _: Byte ->
+            assertThat(prefs.getInt(key1, -1)).isEqualTo(val1)
+            assertThat(prefs.getInt(key2, -1)).isEqualTo(val2)
+
+            assertThat(prefs.getAll()).isEqualTo(mapOf(key1 to val1, key2 to val2))
+
+            99.toByte()
+        }
 
         val dataStore = getDataStoreWithMigrations(listOf(sharedPrefsMigration))
 
@@ -165,12 +136,12 @@
     }
 
     private fun getDataStoreWithMigrations(
-        migrationProducers: List<() -> DataMigration<Byte>>
+        migrations: List<DataMigration<Byte>>
     ): DataStore<Byte> {
         return DataStoreFactory().create(
             produceFile = { datastoreFile },
             serializer = TestingSerializer(),
-            migrationProducers = migrationProducers,
+            migrations = migrations,
             scope = TestCoroutineScope()
         )
     }
diff --git a/datastore/datastore-core/src/main/java/androidx/datastore/DataMigration.kt b/datastore/datastore-core/src/main/java/androidx/datastore/DataMigration.kt
index 892f4cc..aca7f29 100644
--- a/datastore/datastore-core/src/main/java/androidx/datastore/DataMigration.kt
+++ b/datastore/datastore-core/src/main/java/androidx/datastore/DataMigration.kt
@@ -17,15 +17,23 @@
 package androidx.datastore
 
 /**
- * Interface for migrations to DataStore. If you're migrating from SharedPreferences see
- * [SharedPreferencesMigration].
+ * Interface for migrations to DataStore. Methods on this migration ([shouldMigrate], [migrate]
+ * and [cleanUp]) may be called multiple times, so their implementations must be idempotent.
+ * These methods may be called multiple times if DataStore encounters issues when writing the
+ * newly migrated data to disk or if any migration installed in the same DataStore throws an
+ * Exception.
+ *
+ * If you're migrating from SharedPreferences see [SharedPreferencesMigration].
  */
 interface DataMigration<T> {
 
     /**
      * Return whether this migration needs to be performed. If this returns false, no migration or
      * cleanup will occur. Apps should do the cheapest possible check to determine if this migration
-     * should run, since this will be called every time the DataStore is initialized.
+     * should run, since this will be called every time the DataStore is initialized. This method
+     * may be run multiple times when any failure is encountered.
+     *
+     * Note that this will always be called before each call to [migrate].
      *
      * @param currentData the current data (which might already populated from previous runs of this
      * or other migrations)
@@ -37,7 +45,9 @@
      * multiple times. If migrate fails, DataStore will not commit any data to disk, cleanUp will
      * not be called, and the exception will be propagated back to the DataStore call that
      * triggered the migration. Future calls to DataStore will result in DataMigrations being
-     * attempted again.
+     * attempted again. This method may be run multiple times when any failure is encountered.
+     *
+     * Note that this will always be called before a call to [cleanUp].
      *
      * @param currentData the current data (it might be populated from other migrations or from
      * manual changes before this migration was added to the app)
@@ -49,7 +59,8 @@
      * Clean up any old state/data that was migrated into the DataStore. This will not be called
      * if the migration fails. If cleanUp throws an exception, the exception will be propagated
      * back to the DataStore call that triggered the migration and future calls to DataStore will
-     * result in DataMigrations being attempted again.
+     * result in DataMigrations being attempted again. This method may be run multiple times when
+     * any failure is encountered.
      */
     suspend fun cleanUp()
 }
\ No newline at end of file
diff --git a/datastore/datastore-core/src/main/java/androidx/datastore/DataMigrationInitializer.kt b/datastore/datastore-core/src/main/java/androidx/datastore/DataMigrationInitializer.kt
index 42ea187..e5dea0f 100644
--- a/datastore/datastore-core/src/main/java/androidx/datastore/DataMigrationInitializer.kt
+++ b/datastore/datastore-core/src/main/java/androidx/datastore/DataMigrationInitializer.kt
@@ -24,18 +24,13 @@
         /**
          * Creates an initializer from DataMigrations for use with DataStore.
          *
-         * @param migrationTaskFactories A list of functions that return migrations that will be
-         * included in the initializer. If the DataMigration contains any state, the function
-         * should return a new migration each time it is called.
+         * @param migrations A list of migrations that will be included in the initializer.
          * @return The initializer which includes the data migrations returned from the factory
          * functions.
          */
-        fun <T> getInitializer(migrationTaskFactories: List<() -> DataMigration<T>>):
-                suspend (api: InitializerApi<T>) -> Unit {
-            return { api ->
-                val migrations = migrationTaskFactories.map { it() }
-                runMigrations(migrations, api)
-            }
+        fun <T> getInitializer(migrations: List<DataMigration<T>>):
+                suspend (api: InitializerApi<T>) -> Unit = { api ->
+            runMigrations(migrations, api)
         }
 
         private suspend fun <T> runMigrations(
diff --git a/datastore/datastore-core/src/main/java/androidx/datastore/DataStoreFactory.kt b/datastore/datastore-core/src/main/java/androidx/datastore/DataStoreFactory.kt
index 5e7745f..969495e 100644
--- a/datastore/datastore-core/src/main/java/androidx/datastore/DataStoreFactory.kt
+++ b/datastore/datastore-core/src/main/java/androidx/datastore/DataStoreFactory.kt
@@ -44,23 +44,23 @@
      * @param corruptionHandler The corruptionHandler is invoked if DataStore encounters a
      * [CorruptionException] when attempting to read data. CorruptionExceptions are thrown by
      * serializers when data can not be de-serialized.
-     * @param migrationProducers Migrations are run before any access to data can occur. Migrations
-     * must be idempotent.
+     * @param migrations Migrations are run before any access to data can occur. Migrations must
+     * be idempotent.
      * @param scope The scope in which IO operations and transform functions will execute.
      */
-    @JvmOverloads
+    @JvmOverloads // Generate constructors for default params for java users.
     fun <T> create(
         produceFile: () -> File,
         serializer: Serializer<T>,
         corruptionHandler: ReplaceFileCorruptionHandler<T>? = null,
-        migrationProducers: List<() -> DataMigration<T>> = listOf(),
+        migrations: List<DataMigration<T>> = listOf(),
         scope: CoroutineScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
     ): DataStore<T> =
         SingleProcessDataStore(
             produceFile = produceFile,
             serializer = serializer,
             corruptionHandler = corruptionHandler ?: NoOpCorruptionHandler(),
-            initTasksList = listOf(DataMigrationInitializer.getInitializer(migrationProducers)),
+            initTasksList = listOf(DataMigrationInitializer.getInitializer(migrations)),
             scope = scope
         )
 }
\ No newline at end of file
diff --git a/datastore/datastore-core/src/main/java/androidx/datastore/migrations/SharedPreferencesMigration.kt b/datastore/datastore-core/src/main/java/androidx/datastore/migrations/SharedPreferencesMigration.kt
index 4a26721..63826ca 100644
--- a/datastore/datastore-core/src/main/java/androidx/datastore/migrations/SharedPreferencesMigration.kt
+++ b/datastore/datastore-core/src/main/java/androidx/datastore/migrations/SharedPreferencesMigration.kt
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-@file:JvmName("SharedPreferencesMigration")
-
 package androidx.datastore.migrations
 
 import android.content.Context
@@ -25,18 +23,19 @@
 import java.io.IOException
 
 /**
- * A DataMigration which migrates SharedPreferences to DataStore.
+ * DataMigration from SharedPreferences to DataStore.
  *
- * Note: this accesses the SharedPreferences using MODE_PRIVATE.
- */
-internal val MIGRATE_ALL_KEYS = null
-
-/**
- * Returns a factory function which creates a SharedPreferences Data Migration.
+ * Example usage:
+ *
+ * val sharedPrefsMigration = SharedPreferencesMigration(
+ *   context,
+ *   mySharedPreferencesName
+ * ) { prefs: SharedPreferencesView, myData: MyData ->
+ *    myData.toBuilder().setCounter(prefs.getCounter(COUNTER_KEY, default = 0)).build()
+ * }
  *
  * @param context Context used for getting SharedPreferences.
  * @param sharedPreferencesName The name of the SharedPreferences.
- * @param migration The mapping function for the migration.
  * @param keysToMigrate The list of keys to migrate. The keys will be mapped to datastore
  * .Preferences with their same values. If the key is already present in the new Preferences, the key
  * will not be migrated again. If the key is not present in the SharedPreferences it will not be
@@ -48,49 +47,93 @@
  * SharedPreferences to begin with then the (potentially) empty SharedPreferences won't be
  * cleaned up by this option. This functionality is best effort - if there is an issue deleting
  * the SharedPreferences file it will be silently ignored.
+ * @param migrate maps SharedPreferences into T. Implementations should be idempotent
+ * since this may be called multiple times. See [DataMigration.migrate] for more
+ * information. The lambda accepts a SharedPreferencesView which is the view of the
+ * SharedPreferences to migrate from (limited to [keysToMigrate] and a T which represent
+ * the current data. The function must return the migrated data.
  */
-fun <T> SharedPreferencesMigration(
-    context: Context,
-    sharedPreferencesName: String,
-    migration: MigrationFromSharedPreferences<T>,
+class SharedPreferencesMigration<T>
+@JvmOverloads // Generate constructors for default params for java users.
+constructor(
+    private val context: Context,
+    private val sharedPreferencesName: String,
     keysToMigrate: Set<String>? = MIGRATE_ALL_KEYS,
-    deleteEmptyPreferences: Boolean = true
-): () -> DataMigration<T> = {
-    SharedPreferencesDataMigration(
-        context,
-        sharedPreferencesName,
-        migration,
-        keysToMigrate?.toMutableSet(),
-        deleteEmptyPreferences
-    )
-}
+    private val deleteEmptyPreferences: Boolean = true,
+    private val shouldRunMigration: suspend (T) -> Boolean = { true },
+    private val migrate: suspend (SharedPreferencesView, T) -> T
+) : DataMigration<T> {
 
-/**
- * User implemented migration interface. Contains logic for mapping SharedPreferences data to T.
- */
-interface MigrationFromSharedPreferences<T> {
-    /**
-     * Optional method that should return false if the migration should be skipped. This can
-     * be useful to stop unnecessary calls into SharedPreferences. This can be implemented by
-     * including a field in your data that specifies whether your migration has already been
-     * run.
-     *
-     * @param currentData the current data (it might already populated from this or other
-     * migrations)
-     * @return Whether or not this migration should run.
-     */
-    suspend fun shouldMigrate(currentData: T): Boolean = true
+    private val sharedPrefs: SharedPreferences by lazy {
+        context.getSharedPreferences(sharedPreferencesName, Context.MODE_PRIVATE)
+    }
 
-    /**
-     * Perform the migration. Implementations should be idempotent since this may be called
-     * multiple times. See {@code DataMigration#migrate} for more information.
-     *
-     * @param prefs the view of the SharedPreferences to migrate from.
-     * @param currentData the current data (it might be populated from other migrations or from
-     * manual changes before this migration was added to the app)
-     * @return The migrated data.
-     */
-    suspend fun migrate(prefs: SharedPreferencesView, currentData: T): T
+    private val keySet: MutableSet<String> by lazy {
+        (keysToMigrate ?: sharedPrefs.all.keys).toMutableSet()
+    }
+
+    override suspend fun shouldMigrate(currentData: T): Boolean {
+        if (!shouldRunMigration(currentData)) {
+            return false
+        }
+
+        return keySet.any(sharedPrefs::contains)
+    }
+
+    override suspend fun migrate(currentData: T): T =
+        migrate(
+            SharedPreferencesView(
+                sharedPrefs,
+                keySet
+            ), currentData
+        )
+
+    @Throws(IOException::class)
+    override suspend fun cleanUp() {
+        val sharedPrefsEditor = sharedPrefs.edit()
+
+        for (key in keySet) {
+            sharedPrefsEditor.remove(key)
+        }
+
+        if (!sharedPrefsEditor.commit()) {
+            throw IOException(
+                "Unable to delete migrated keys from SharedPreferences: $sharedPreferencesName"
+            )
+        }
+
+        if (deleteEmptyPreferences && sharedPrefs.all.isEmpty()) {
+            deleteSharedPreferences(context, sharedPreferencesName)
+        }
+
+        keySet.clear()
+    }
+
+    private fun deleteSharedPreferences(context: Context, name: String) {
+        if (android.os.Build.VERSION.SDK_INT >= 24) {
+            if (!context.deleteSharedPreferences(name)) {
+                throw IOException("Unable to delete SharedPreferences: $name")
+            }
+            return
+        }
+
+        // Context.deleteSharedPreferences is SDK 24+, so we have to reproduce the definition
+        val prefsFile = getSharedPrefsFile(context, name)
+        val prefsBackup = getSharedPrefsBackup(prefsFile)
+
+        // Silently continue if we aren't able to delete the Shared Preferences File.
+        prefsFile.delete()
+        prefsBackup.delete()
+    }
+
+    // ContextImpl.getSharedPreferencesPath is private, so we have to reproduce the definition
+    private fun getSharedPrefsFile(context: Context, name: String): File {
+        val prefsDir = File(context.applicationInfo.dataDir, "shared_prefs")
+        return File(prefsDir, "$name.xml")
+    }
+
+    // SharedPreferencesImpl.makeBackupFile is private, so we have to reproduce the definition
+    private fun getSharedPrefsBackup(prefsFile: File) = File(prefsFile.path + ".bak")
 }
 
 /**
@@ -183,81 +226,4 @@
     }
 }
 
-private class SharedPreferencesDataMigration<T> internal constructor(
-    private val context: Context,
-    private val sharedPreferencesName: String,
-    private val migration: MigrationFromSharedPreferences<T>,
-    keysToMigrate: MutableSet<String>?,
-    private val deleteEmptyPreferences: Boolean
-) : DataMigration<T> {
-    private val sharedPrefs: SharedPreferences by lazy {
-        context.getSharedPreferences(sharedPreferencesName, Context.MODE_PRIVATE)
-    }
-
-    private val keySet: MutableSet<String> by lazy {
-        keysToMigrate ?: sharedPrefs.all.keys.toMutableSet()
-    }
-
-    override suspend fun shouldMigrate(currentData: T): Boolean {
-        if (!migration.shouldMigrate(currentData)) {
-            return false
-        }
-
-        return keySet.any(sharedPrefs::contains)
-    }
-
-    override suspend fun migrate(currentData: T): T =
-        migration.migrate(
-            SharedPreferencesView(
-                sharedPrefs,
-                keySet
-            ), currentData
-        )
-
-    @Throws(IOException::class)
-    override suspend fun cleanUp() {
-        val sharedPrefsEditor = sharedPrefs.edit()
-
-        for (key in keySet) {
-            sharedPrefsEditor.remove(key)
-        }
-
-        if (!sharedPrefsEditor.commit()) {
-            throw IOException(
-                "Unable to delete migrated keys from SharedPreferences: $sharedPreferencesName"
-            )
-        }
-
-        if (deleteEmptyPreferences && sharedPrefs.all.isEmpty()) {
-            deleteSharedPreferences(context, sharedPreferencesName)
-        }
-
-        keySet.clear()
-    }
-
-    private fun deleteSharedPreferences(context: Context, name: String) {
-        if (android.os.Build.VERSION.SDK_INT >= 24) {
-            if (!context.deleteSharedPreferences(name)) {
-                throw IOException("Unable to delete SharedPreferences: $name")
-            }
-            return
-        }
-
-        // Context.deleteSharedPreferences is SDK 24+, so we have to reproduce the definition
-        val prefsFile = getSharedPrefsFile(context, name)
-        val prefsBackup = getSharedPrefsBackup(prefsFile)
-
-        // Silently continue if we aren't able to delete the Shared Preferences File.
-        prefsFile.delete()
-        prefsBackup.delete()
-    }
-
-    // ContextImpl.getSharedPreferencesPath is private, so we have to reproduce the definition
-    private fun getSharedPrefsFile(context: Context, name: String): File {
-        val prefsDir = File(context.applicationInfo.dataDir, "shared_prefs")
-        return File(prefsDir, "$name.xml")
-    }
-
-    // SharedPreferencesImpl.makeBackupFile is private, so we have to reproduce the definition
-    private fun getSharedPrefsBackup(prefsFile: File) = File(prefsFile.path + ".bak")
-}
+internal val MIGRATE_ALL_KEYS = null
diff --git a/datastore/datastore-core/src/test/java/androidx/datastore/DataMigrationInitializerTest.kt b/datastore/datastore-core/src/test/java/androidx/datastore/DataMigrationInitializerTest.kt
index 0729f0f..93eaf0d 100644
--- a/datastore/datastore-core/src/test/java/androidx/datastore/DataMigrationInitializerTest.kt
+++ b/datastore/datastore-core/src/test/java/androidx/datastore/DataMigrationInitializerTest.kt
@@ -54,7 +54,7 @@
         val store = newDataStore(
             initTasksList = listOf(
                 DataMigrationInitializer.getInitializer(
-                    listOf { migrateTo100 }
+                    listOf(migrateTo100)
                 )
             )
         )
@@ -70,7 +70,7 @@
         val store = newDataStore(
             initTasksList = listOf(
                 DataMigrationInitializer.getInitializer(
-                    listOf({ migratePlus2 }, { migratePlus3 })
+                    listOf(migratePlus2, migratePlus3)
                 )
             )
         )
@@ -90,7 +90,7 @@
 
         val store = newDataStore(
             initTasksList = listOf(
-                DataMigrationInitializer.getInitializer(listOf({ noOpMigration }))
+                DataMigrationInitializer.getInitializer(listOf(noOpMigration))
             )
         )
 
@@ -114,7 +114,7 @@
 
         val store = newDataStore(
             initTasksList = listOf(
-                DataMigrationInitializer.getInitializer(listOf({ noOpMigration }))
+                DataMigrationInitializer.getInitializer(listOf(noOpMigration))
             )
         )
 
@@ -140,7 +140,7 @@
         serializer.failingWrite = true
         val store = newDataStore(
             initTasksList = listOf(
-                DataMigrationInitializer.getInitializer(listOf({ noOpMigration }))
+                DataMigrationInitializer.getInitializer(listOf(noOpMigration))
             ),
             serializer = serializer
         )
@@ -162,7 +162,7 @@
 
         val store = newDataStore(
             initTasksList = listOf(
-                DataMigrationInitializer.getInitializer(listOf({ cleanUpFailingMigration }))
+                DataMigrationInitializer.getInitializer(listOf(cleanUpFailingMigration))
             )
         )
 
@@ -175,42 +175,13 @@
 
         val store = newDataStore(
             initTasksList = listOf(
-                DataMigrationInitializer.getInitializer(listOf({ neverRunMigration }))
+                DataMigrationInitializer.getInitializer(listOf(neverRunMigration))
             )
         )
 
         assertThat(store.data.first()).isEqualTo(0)
     }
 
-    @Test
-    fun testNewDataMigrationUsedOnFailure() = runBlockingTest {
-        val migrationFactory =
-            {
-                var byte: Byte = 99
-                val migration = TestingDataMigration(migration = {
-                    val unmodifiedByte = byte
-                    byte = byte.inc()
-                    unmodifiedByte
-                })
-                migration
-            }
-
-        val store = newDataStore(
-            initTasksList = listOf(
-                DataMigrationInitializer.getInitializer(listOf(migrationFactory))
-            ),
-            serializer = serializer
-        )
-
-        serializer.failingWrite = true
-
-        assertThrows<IOException> { store.data.first() }
-
-        serializer.failingWrite = false
-
-        assertThat(store.data.first()).isEqualTo(99)
-    }
-
     private fun newDataStore(
         initTasksList: List<suspend (api: InitializerApi<Byte>) -> Unit> = listOf(),
         serializer: TestingSerializer = TestingSerializer()
diff --git a/datastore/datastore-core/src/test/java/androidx/datastore/DataStoreFactoryTest.kt b/datastore/datastore-core/src/test/java/androidx/datastore/DataStoreFactoryTest.kt
index 4d17579..f58247c 100644
--- a/datastore/datastore-core/src/test/java/androidx/datastore/DataStoreFactoryTest.kt
+++ b/datastore/datastore-core/src/test/java/androidx/datastore/DataStoreFactoryTest.kt
@@ -85,26 +85,22 @@
 
         val migratedByte = 1
 
-        val migratePlus2 = {
-            object : DataMigration<Byte> {
+        val migratePlus2 = object : DataMigration<Byte> {
                 override suspend fun shouldMigrate(currentData: Byte) = true
                 override suspend fun migrate(currentData: Byte) = currentData.inc().inc()
                 override suspend fun cleanUp() {}
             }
-        }
-        val migrateMinus1 = {
-            object : DataMigration<Byte> {
+        val migrateMinus1 = object : DataMigration<Byte> {
                 override suspend fun shouldMigrate(currentData: Byte) = true
 
                 override suspend fun migrate(currentData: Byte) = currentData.dec()
 
                 override suspend fun cleanUp() {}
             }
-        }
 
         val store = factory.create(
             produceFile = { testFile },
-            migrationProducers = listOf(migratePlus2, migrateMinus1),
+            migrations = listOf(migratePlus2, migrateMinus1),
             scope = dataStoreScope,
             serializer = TestingSerializer()
         )
diff --git a/datastore/datastore-preferences/api/current.txt b/datastore/datastore-preferences/api/current.txt
index 53a9826..217726b 100644
--- a/datastore/datastore-preferences/api/current.txt
+++ b/datastore/datastore-preferences/api/current.txt
@@ -3,8 +3,8 @@
 
   public final class PreferenceDataStoreFactory {
     ctor public PreferenceDataStoreFactory();
-    method public androidx.datastore.DataStore<androidx.datastore.preferences.Preferences> create(kotlin.jvm.functions.Function0<? extends java.io.File> produceFile, androidx.datastore.handlers.ReplaceFileCorruptionHandler<androidx.datastore.preferences.Preferences>? corruptionHandler = null, java.util.List<? extends kotlin.jvm.functions.Function0<? extends androidx.datastore.DataMigration<androidx.datastore.preferences.Preferences>>> migrationProducers = listOf(), kotlinx.coroutines.CoroutineScope scope = CoroutineScope(Dispatchers.IO + SupervisorJob()));
-    method public androidx.datastore.DataStore<androidx.datastore.preferences.Preferences> create(kotlin.jvm.functions.Function0<? extends java.io.File> produceFile, androidx.datastore.handlers.ReplaceFileCorruptionHandler<androidx.datastore.preferences.Preferences>? corruptionHandler = null, java.util.List<? extends kotlin.jvm.functions.Function0<? extends androidx.datastore.DataMigration<androidx.datastore.preferences.Preferences>>> migrationProducers = listOf());
+    method public androidx.datastore.DataStore<androidx.datastore.preferences.Preferences> create(kotlin.jvm.functions.Function0<? extends java.io.File> produceFile, androidx.datastore.handlers.ReplaceFileCorruptionHandler<androidx.datastore.preferences.Preferences>? corruptionHandler = null, java.util.List<? extends androidx.datastore.DataMigration<androidx.datastore.preferences.Preferences>> migrations = listOf(), kotlinx.coroutines.CoroutineScope scope = CoroutineScope(Dispatchers.IO + SupervisorJob()));
+    method public androidx.datastore.DataStore<androidx.datastore.preferences.Preferences> create(kotlin.jvm.functions.Function0<? extends java.io.File> produceFile, androidx.datastore.handlers.ReplaceFileCorruptionHandler<androidx.datastore.preferences.Preferences>? corruptionHandler = null, java.util.List<? extends androidx.datastore.DataMigration<androidx.datastore.preferences.Preferences>> migrations = listOf());
     method public androidx.datastore.DataStore<androidx.datastore.preferences.Preferences> create(kotlin.jvm.functions.Function0<? extends java.io.File> produceFile, androidx.datastore.handlers.ReplaceFileCorruptionHandler<androidx.datastore.preferences.Preferences>? corruptionHandler = null);
     method public androidx.datastore.DataStore<androidx.datastore.preferences.Preferences> create(kotlin.jvm.functions.Function0<? extends java.io.File> produceFile);
   }
@@ -40,8 +40,10 @@
     method public androidx.datastore.preferences.Preferences empty();
   }
 
-  public final class SharedPreferencesToPreferencesKt {
-    method public static kotlin.jvm.functions.Function0<androidx.datastore.DataMigration<androidx.datastore.preferences.Preferences>> SharedPreferencesMigration(android.content.Context context, String sharedPreferencesName, java.util.Set<java.lang.String>? keysToMigrate = SharedPreferencesToPreferences.MIGRATE_ALL_KEYS, boolean deleteEmptyPreferences = true);
+  public final class SharedPreferencesMigrationKt {
+    method public static androidx.datastore.migrations.SharedPreferencesMigration<androidx.datastore.preferences.Preferences> SharedPreferencesMigration(android.content.Context context, String sharedPreferencesName, java.util.Set<java.lang.String>? keysToMigrate = MIGRATE_ALL_KEYS, boolean deleteEmptyPreferences = true);
+    method public static androidx.datastore.migrations.SharedPreferencesMigration<androidx.datastore.preferences.Preferences> SharedPreferencesMigration(android.content.Context context, String sharedPreferencesName, java.util.Set<java.lang.String>? keysToMigrate = MIGRATE_ALL_KEYS);
+    method public static androidx.datastore.migrations.SharedPreferencesMigration<androidx.datastore.preferences.Preferences> SharedPreferencesMigration(android.content.Context context, String sharedPreferencesName);
   }
 
 }
diff --git a/datastore/datastore-preferences/api/public_plus_experimental_current.txt b/datastore/datastore-preferences/api/public_plus_experimental_current.txt
index 53a9826..217726b 100644
--- a/datastore/datastore-preferences/api/public_plus_experimental_current.txt
+++ b/datastore/datastore-preferences/api/public_plus_experimental_current.txt
@@ -3,8 +3,8 @@
 
   public final class PreferenceDataStoreFactory {
     ctor public PreferenceDataStoreFactory();
-    method public androidx.datastore.DataStore<androidx.datastore.preferences.Preferences> create(kotlin.jvm.functions.Function0<? extends java.io.File> produceFile, androidx.datastore.handlers.ReplaceFileCorruptionHandler<androidx.datastore.preferences.Preferences>? corruptionHandler = null, java.util.List<? extends kotlin.jvm.functions.Function0<? extends androidx.datastore.DataMigration<androidx.datastore.preferences.Preferences>>> migrationProducers = listOf(), kotlinx.coroutines.CoroutineScope scope = CoroutineScope(Dispatchers.IO + SupervisorJob()));
-    method public androidx.datastore.DataStore<androidx.datastore.preferences.Preferences> create(kotlin.jvm.functions.Function0<? extends java.io.File> produceFile, androidx.datastore.handlers.ReplaceFileCorruptionHandler<androidx.datastore.preferences.Preferences>? corruptionHandler = null, java.util.List<? extends kotlin.jvm.functions.Function0<? extends androidx.datastore.DataMigration<androidx.datastore.preferences.Preferences>>> migrationProducers = listOf());
+    method public androidx.datastore.DataStore<androidx.datastore.preferences.Preferences> create(kotlin.jvm.functions.Function0<? extends java.io.File> produceFile, androidx.datastore.handlers.ReplaceFileCorruptionHandler<androidx.datastore.preferences.Preferences>? corruptionHandler = null, java.util.List<? extends androidx.datastore.DataMigration<androidx.datastore.preferences.Preferences>> migrations = listOf(), kotlinx.coroutines.CoroutineScope scope = CoroutineScope(Dispatchers.IO + SupervisorJob()));
+    method public androidx.datastore.DataStore<androidx.datastore.preferences.Preferences> create(kotlin.jvm.functions.Function0<? extends java.io.File> produceFile, androidx.datastore.handlers.ReplaceFileCorruptionHandler<androidx.datastore.preferences.Preferences>? corruptionHandler = null, java.util.List<? extends androidx.datastore.DataMigration<androidx.datastore.preferences.Preferences>> migrations = listOf());
     method public androidx.datastore.DataStore<androidx.datastore.preferences.Preferences> create(kotlin.jvm.functions.Function0<? extends java.io.File> produceFile, androidx.datastore.handlers.ReplaceFileCorruptionHandler<androidx.datastore.preferences.Preferences>? corruptionHandler = null);
     method public androidx.datastore.DataStore<androidx.datastore.preferences.Preferences> create(kotlin.jvm.functions.Function0<? extends java.io.File> produceFile);
   }
@@ -40,8 +40,10 @@
     method public androidx.datastore.preferences.Preferences empty();
   }
 
-  public final class SharedPreferencesToPreferencesKt {
-    method public static kotlin.jvm.functions.Function0<androidx.datastore.DataMigration<androidx.datastore.preferences.Preferences>> SharedPreferencesMigration(android.content.Context context, String sharedPreferencesName, java.util.Set<java.lang.String>? keysToMigrate = SharedPreferencesToPreferences.MIGRATE_ALL_KEYS, boolean deleteEmptyPreferences = true);
+  public final class SharedPreferencesMigrationKt {
+    method public static androidx.datastore.migrations.SharedPreferencesMigration<androidx.datastore.preferences.Preferences> SharedPreferencesMigration(android.content.Context context, String sharedPreferencesName, java.util.Set<java.lang.String>? keysToMigrate = MIGRATE_ALL_KEYS, boolean deleteEmptyPreferences = true);
+    method public static androidx.datastore.migrations.SharedPreferencesMigration<androidx.datastore.preferences.Preferences> SharedPreferencesMigration(android.content.Context context, String sharedPreferencesName, java.util.Set<java.lang.String>? keysToMigrate = MIGRATE_ALL_KEYS);
+    method public static androidx.datastore.migrations.SharedPreferencesMigration<androidx.datastore.preferences.Preferences> SharedPreferencesMigration(android.content.Context context, String sharedPreferencesName);
   }
 
 }
diff --git a/datastore/datastore-preferences/api/restricted_current.txt b/datastore/datastore-preferences/api/restricted_current.txt
index 53a9826..217726b 100644
--- a/datastore/datastore-preferences/api/restricted_current.txt
+++ b/datastore/datastore-preferences/api/restricted_current.txt
@@ -3,8 +3,8 @@
 
   public final class PreferenceDataStoreFactory {
     ctor public PreferenceDataStoreFactory();
-    method public androidx.datastore.DataStore<androidx.datastore.preferences.Preferences> create(kotlin.jvm.functions.Function0<? extends java.io.File> produceFile, androidx.datastore.handlers.ReplaceFileCorruptionHandler<androidx.datastore.preferences.Preferences>? corruptionHandler = null, java.util.List<? extends kotlin.jvm.functions.Function0<? extends androidx.datastore.DataMigration<androidx.datastore.preferences.Preferences>>> migrationProducers = listOf(), kotlinx.coroutines.CoroutineScope scope = CoroutineScope(Dispatchers.IO + SupervisorJob()));
-    method public androidx.datastore.DataStore<androidx.datastore.preferences.Preferences> create(kotlin.jvm.functions.Function0<? extends java.io.File> produceFile, androidx.datastore.handlers.ReplaceFileCorruptionHandler<androidx.datastore.preferences.Preferences>? corruptionHandler = null, java.util.List<? extends kotlin.jvm.functions.Function0<? extends androidx.datastore.DataMigration<androidx.datastore.preferences.Preferences>>> migrationProducers = listOf());
+    method public androidx.datastore.DataStore<androidx.datastore.preferences.Preferences> create(kotlin.jvm.functions.Function0<? extends java.io.File> produceFile, androidx.datastore.handlers.ReplaceFileCorruptionHandler<androidx.datastore.preferences.Preferences>? corruptionHandler = null, java.util.List<? extends androidx.datastore.DataMigration<androidx.datastore.preferences.Preferences>> migrations = listOf(), kotlinx.coroutines.CoroutineScope scope = CoroutineScope(Dispatchers.IO + SupervisorJob()));
+    method public androidx.datastore.DataStore<androidx.datastore.preferences.Preferences> create(kotlin.jvm.functions.Function0<? extends java.io.File> produceFile, androidx.datastore.handlers.ReplaceFileCorruptionHandler<androidx.datastore.preferences.Preferences>? corruptionHandler = null, java.util.List<? extends androidx.datastore.DataMigration<androidx.datastore.preferences.Preferences>> migrations = listOf());
     method public androidx.datastore.DataStore<androidx.datastore.preferences.Preferences> create(kotlin.jvm.functions.Function0<? extends java.io.File> produceFile, androidx.datastore.handlers.ReplaceFileCorruptionHandler<androidx.datastore.preferences.Preferences>? corruptionHandler = null);
     method public androidx.datastore.DataStore<androidx.datastore.preferences.Preferences> create(kotlin.jvm.functions.Function0<? extends java.io.File> produceFile);
   }
@@ -40,8 +40,10 @@
     method public androidx.datastore.preferences.Preferences empty();
   }
 
-  public final class SharedPreferencesToPreferencesKt {
-    method public static kotlin.jvm.functions.Function0<androidx.datastore.DataMigration<androidx.datastore.preferences.Preferences>> SharedPreferencesMigration(android.content.Context context, String sharedPreferencesName, java.util.Set<java.lang.String>? keysToMigrate = SharedPreferencesToPreferences.MIGRATE_ALL_KEYS, boolean deleteEmptyPreferences = true);
+  public final class SharedPreferencesMigrationKt {
+    method public static androidx.datastore.migrations.SharedPreferencesMigration<androidx.datastore.preferences.Preferences> SharedPreferencesMigration(android.content.Context context, String sharedPreferencesName, java.util.Set<java.lang.String>? keysToMigrate = MIGRATE_ALL_KEYS, boolean deleteEmptyPreferences = true);
+    method public static androidx.datastore.migrations.SharedPreferencesMigration<androidx.datastore.preferences.Preferences> SharedPreferencesMigration(android.content.Context context, String sharedPreferencesName, java.util.Set<java.lang.String>? keysToMigrate = MIGRATE_ALL_KEYS);
+    method public static androidx.datastore.migrations.SharedPreferencesMigration<androidx.datastore.preferences.Preferences> SharedPreferencesMigration(android.content.Context context, String sharedPreferencesName);
   }
 
 }
diff --git a/datastore/datastore-preferences/src/androidTest/java/androidx/datastore/preferences/SharedPreferencesToPreferencesTest.kt b/datastore/datastore-preferences/src/androidTest/java/androidx/datastore/preferences/SharedPreferencesToPreferencesTest.kt
index 10f9c23..c92da6e 100644
--- a/datastore/datastore-preferences/src/androidTest/java/androidx/datastore/preferences/SharedPreferencesToPreferencesTest.kt
+++ b/datastore/datastore-preferences/src/androidTest/java/androidx/datastore/preferences/SharedPreferencesToPreferencesTest.kt
@@ -391,11 +391,11 @@
     }
 
     private fun getDataStoreWithMigrations(
-        migrationProducers: List<() -> DataMigration<Preferences>>
+        migrations: List<DataMigration<Preferences>>
     ): DataStore<Preferences> {
         return PreferenceDataStoreFactory().create(
             produceFile = { datastoreFile },
-            migrationProducers = migrationProducers,
+            migrations = migrations,
             scope = TestCoroutineScope()
         )
     }
diff --git a/datastore/datastore-preferences/src/main/java/androidx/datastore/preferences/PreferenceDataStoreFactory.kt b/datastore/datastore-preferences/src/main/java/androidx/datastore/preferences/PreferenceDataStoreFactory.kt
index 5c77987..7de4135 100644
--- a/datastore/datastore-preferences/src/main/java/androidx/datastore/preferences/PreferenceDataStoreFactory.kt
+++ b/datastore/datastore-preferences/src/main/java/androidx/datastore/preferences/PreferenceDataStoreFactory.kt
@@ -43,16 +43,16 @@
      * @param corruptionHandler The corruptionHandler is invoked if DataStore encounters a [CorruptionException] when
      * attempting to read data. CorruptionExceptions are thrown by serializers when data can
      * not be de-serialized.
-     * @param migrationProducers Migrations are run before any access to data can occur. Each
+     * @param migrations are run before any access to data can occur. Each
      * producer and migration may be run more than once whether or not it already succeeded
      * (potentially because another migration failed or a write to disk failed.)
      * @param scope The scope in which IO operations and transform functions will execute.
      */
-    @JvmOverloads
+    @JvmOverloads // Generate methods for default params for java users.
     fun create(
         produceFile: () -> File,
         corruptionHandler: ReplaceFileCorruptionHandler<Preferences>? = null,
-        migrationProducers: List<() -> DataMigration<Preferences>> = listOf(),
+        migrations: List<DataMigration<Preferences>> = listOf(),
         scope: CoroutineScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
     ): DataStore<Preferences> =
         dataStoreFactory.create(
@@ -66,7 +66,7 @@
             },
             serializer = PreferencesSerializer,
             corruptionHandler = corruptionHandler,
-            migrationProducers = migrationProducers,
+            migrations = migrations,
             scope = scope
         )
 }
\ No newline at end of file
diff --git a/datastore/datastore-preferences/src/main/java/androidx/datastore/preferences/SharedPreferencesMigration.kt b/datastore/datastore-preferences/src/main/java/androidx/datastore/preferences/SharedPreferencesMigration.kt
new file mode 100644
index 0000000..0e2a46c
--- /dev/null
+++ b/datastore/datastore-preferences/src/main/java/androidx/datastore/preferences/SharedPreferencesMigration.kt
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2020 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.datastore.preferences
+
+import android.content.Context
+import androidx.datastore.migrations.SharedPreferencesView
+import androidx.datastore.migrations.SharedPreferencesMigration
+
+/**
+ * Creates a SharedPreferencesMigration for DataStore<Preferences>.
+ *
+ * @param context Context used for getting SharedPreferences.
+ * @param sharedPreferencesName The name of the SharedPreferences.
+ * @param keysToMigrate The list of keys to migrate. The keys will be mapped to datastore.Preferences with
+ * their same values. If the key is already present in the new Preferences, the key
+ * will not be migrated again. If the key is not present in the SharedPreferences it
+ * will not be migrated. If keysToMigrate is not set, all keys will be migrated from the existing
+ * SharedPreferences.
+ * @param deleteEmptyPreferences If enabled and the SharedPreferences are empty (i.e. no remaining
+ * keys) after this migration runs, the leftover SharedPreferences file is deleted. Note that
+ * this cleanup runs only if the migration itself runs, i.e., if the keys were never in
+ * SharedPreferences to begin with then the (potentially) empty SharedPreferences
+ * won't be cleaned up by this option. This functionality is best effort - if there
+ * is an issue deleting the SharedPreferences file it will be silently ignored.
+ *
+ * TODO(rohitsat): determine whether to remove the deleteEmptyPreferences option.
+ */
+@JvmOverloads // Generate methods for default params for java users.
+fun SharedPreferencesMigration(
+    context: Context,
+    sharedPreferencesName: String,
+    keysToMigrate: Set<String>? = MIGRATE_ALL_KEYS,
+    deleteEmptyPreferences: Boolean = true
+): SharedPreferencesMigration<Preferences> {
+    return SharedPreferencesMigration(
+        context = context,
+        sharedPreferencesName = sharedPreferencesName,
+        keysToMigrate = keysToMigrate,
+        deleteEmptyPreferences = deleteEmptyPreferences,
+        shouldRunMigration = { prefs ->
+            // If any key hasn't been migrated to currentData, we can't skip the migration. If
+            // the key set is not specified, we can't skip the migration.
+            keysToMigrate?.any { it !in prefs } ?: true
+        },
+        migrate = { sharedPrefs: SharedPreferencesView, currentData: Preferences ->
+            // prefs.getAll is already filtered to our key set.
+            val preferencesToMigrate =
+                sharedPrefs.getAll().filter { (key, _) -> key !in currentData }
+
+            val preferencesBuilder = currentData.toBuilder()
+            for ((key, value) in preferencesToMigrate) {
+                when (value) {
+                    is Boolean -> preferencesBuilder.setBoolean(key, value)
+                    is Float -> preferencesBuilder.setFloat(key, value)
+                    is Int -> preferencesBuilder.setInt(key, value)
+                    is Long -> preferencesBuilder.setLong(key, value)
+                    is String -> preferencesBuilder.setString(key, value)
+                    is Set<*> ->
+                        @Suppress("UNCHECKED_CAST")
+                        preferencesBuilder.setStringSet(key, value.toSet() as Set<String>)
+                }
+            }
+
+            preferencesBuilder.build()
+        })
+}
+
+internal val MIGRATE_ALL_KEYS = null
\ No newline at end of file
diff --git a/datastore/datastore-preferences/src/main/java/androidx/datastore/preferences/SharedPreferencesToPreferences.kt b/datastore/datastore-preferences/src/main/java/androidx/datastore/preferences/SharedPreferencesToPreferences.kt
deleted file mode 100644
index 0a70ea3..0000000
--- a/datastore/datastore-preferences/src/main/java/androidx/datastore/preferences/SharedPreferencesToPreferences.kt
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright 2020 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.datastore.preferences
-
-import android.content.Context
-import androidx.datastore.DataMigration
-import androidx.datastore.migrations.MigrationFromSharedPreferences
-import androidx.datastore.migrations.SharedPreferencesView
-import androidx.datastore.migrations.SharedPreferencesMigration
-
-/**
- * Creates a SharedPreferencesMigration for DataStore<Preferences>.
- *
- * @param context Context used for getting SharedPreferences.
- * @param sharedPreferencesName The name of the SharedPreferences.
- * @param keysToMigrate The list of keys to migrate. The keys will be mapped to datastore.Preferences with
- * their same values. If the key is already present in the new Preferences, the key
- * will not be migrated again. If the key is not present in the SharedPreferences it
- * will not be migrated. If keysToMigrate is not set, all keys will be migrated from the existing
- * SharedPreferences.
- * @param deleteEmptyPreferences If enabled and the SharedPreferences are empty (i.e. no remaining
- * keys) after this migration runs, the leftover SharedPreferences file is deleted. Note that
- * this cleanup runs only if the migration itself runs, i.e., if the keys were never in
- * SharedPreferences to begin with then the (potentially) empty SharedPreferences
- * won't be cleaned up by this option. This functionality is best effort - if there
- * is an issue deleting the SharedPreferences file it will be silently ignored.
- */
-fun SharedPreferencesMigration(
-    context: Context,
-    sharedPreferencesName: String,
-    keysToMigrate: Set<String>? = SharedPreferencesToPreferences.MIGRATE_ALL_KEYS,
-    deleteEmptyPreferences: Boolean = true
-): () -> DataMigration<Preferences> {
-    return SharedPreferencesMigration(
-        context,
-        sharedPreferencesName,
-        SharedPreferencesToPreferences(keysToMigrate),
-        keysToMigrate,
-        deleteEmptyPreferences
-    )
-}
-
-/**
- * A DataMigration which migrates SharedPreferences to DataStore.
- *
- * Note: this accesses the SharedPreferences using MODE_PRIVATE.
- */
-internal class SharedPreferencesToPreferences(
-    private val keysToMigrate: Set<String>?
-) : MigrationFromSharedPreferences<Preferences> {
-
-    companion object {
-        internal val MIGRATE_ALL_KEYS = null
-    }
-
-    override suspend fun shouldMigrate(currentData: Preferences): Boolean {
-        if (keysToMigrate == null) {
-            // We need to migrate all keys from the SharedPreferences.
-            return true
-        }
-
-        // If any key hasn't been migrated to currentData, we can't skip the migration.
-        return keysToMigrate.any { it !in currentData }
-    }
-
-    override suspend fun migrate(
-        prefs: SharedPreferencesView,
-        currentData: Preferences
-    ): Preferences {
-        // prefs.getAll is already filtered to our key set.
-        val preferencesToMigrate = prefs.getAll().filter { (key, _) -> key !in currentData }
-
-        val preferencesBuilder = currentData.toBuilder()
-        for ((key, value) in preferencesToMigrate) {
-            when (value) {
-                is Boolean -> preferencesBuilder.setBoolean(key, value)
-                is Float -> preferencesBuilder.setFloat(key, value)
-                is Int -> preferencesBuilder.setInt(key, value)
-                is Long -> preferencesBuilder.setLong(key, value)
-                is String -> preferencesBuilder.setString(key, value)
-                is Set<*> ->
-                    @Suppress("UNCHECKED_CAST")
-                    preferencesBuilder.setStringSet(key, value.toSet() as Set<String>)
-            }
-        }
-
-        return preferencesBuilder.build()
-    }
-}
\ No newline at end of file
diff --git a/datastore/datastore-preferences/src/test/java/androidx/datastore/preferences/PreferenceDataStoreFactoryTest.kt b/datastore/datastore-preferences/src/test/java/androidx/datastore/preferences/PreferenceDataStoreFactoryTest.kt
index e200d12..7ae14dc 100644
--- a/datastore/datastore-preferences/src/test/java/androidx/datastore/preferences/PreferenceDataStoreFactoryTest.kt
+++ b/datastore/datastore-preferences/src/test/java/androidx/datastore/preferences/PreferenceDataStoreFactoryTest.kt
@@ -91,30 +91,27 @@
             .setBoolean("boolean_key", true)
             .build()
 
-        val migrateTo5 = {
-            object : DataMigration<Preferences> {
-                override suspend fun shouldMigrate(currentData: Preferences) = true
+        val migrateTo5 = object : DataMigration<Preferences> {
+            override suspend fun shouldMigrate(currentData: Preferences) = true
 
-                override suspend fun migrate(currentData: Preferences) =
-                    currentData.toBuilder().setString("string_key", "value").build()
+            override suspend fun migrate(currentData: Preferences) =
+                currentData.toBuilder().setString("string_key", "value").build()
 
-                override suspend fun cleanUp() {}
-            }
+            override suspend fun cleanUp() {}
         }
-        val migratePlus1 = {
-            object : DataMigration<Preferences> {
-                override suspend fun shouldMigrate(currentData: Preferences) = true
 
-                override suspend fun migrate(currentData: Preferences) =
-                    currentData.toBuilder().setBoolean("boolean_key", true).build()
+        val migratePlus1 = object : DataMigration<Preferences> {
+            override suspend fun shouldMigrate(currentData: Preferences) = true
 
-                override suspend fun cleanUp() {}
-            }
+            override suspend fun migrate(currentData: Preferences) =
+                currentData.toBuilder().setBoolean("boolean_key", true).build()
+
+            override suspend fun cleanUp() {}
         }
 
         val store = factory.create(
             produceFile = { testFile },
-            migrationProducers = listOf(migrateTo5, migratePlus1),
+            migrations = listOf(migrateTo5, migratePlus1),
             scope = dataStoreScope
         )
 
diff --git a/enterprise/feedback/api/1.1.0-alpha01.txt b/enterprise/feedback/api/1.1.0-alpha01.txt
index 1cd14ea..4a23dc1 100644
--- a/enterprise/feedback/api/1.1.0-alpha01.txt
+++ b/enterprise/feedback/api/1.1.0-alpha01.txt
@@ -25,11 +25,21 @@
     method public abstract androidx.enterprise.feedback.KeyedAppState.KeyedAppStateBuilder setSeverity(int);
   }
 
+  public interface KeyedAppStatesCallback {
+    method public void onResult(int, Throwable?);
+    field public static final int STATUS_EXCEEDED_BUFFER_ERROR = 3; // 0x3
+    field public static final int STATUS_SUCCESS = 0; // 0x0
+    field public static final int STATUS_TRANSACTION_TOO_LARGE_ERROR = 2; // 0x2
+    field public static final int STATUS_UNKNOWN_ERROR = 1; // 0x1
+  }
+
   public abstract class KeyedAppStatesReporter {
     method public static androidx.enterprise.feedback.KeyedAppStatesReporter create(android.content.Context);
     method public static androidx.enterprise.feedback.KeyedAppStatesReporter create(android.content.Context, java.util.concurrent.Executor);
-    method public abstract void setStates(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>);
-    method public abstract void setStatesImmediate(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>);
+    method @Deprecated public abstract void setStates(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>);
+    method public void setStates(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>, androidx.enterprise.feedback.KeyedAppStatesCallback?);
+    method @Deprecated public abstract void setStatesImmediate(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>);
+    method public void setStatesImmediate(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>, androidx.enterprise.feedback.KeyedAppStatesCallback?);
   }
 
   public abstract class KeyedAppStatesService extends android.app.Service {
diff --git a/enterprise/feedback/api/current.txt b/enterprise/feedback/api/current.txt
index 1cd14ea..4a23dc1 100644
--- a/enterprise/feedback/api/current.txt
+++ b/enterprise/feedback/api/current.txt
@@ -25,11 +25,21 @@
     method public abstract androidx.enterprise.feedback.KeyedAppState.KeyedAppStateBuilder setSeverity(int);
   }
 
+  public interface KeyedAppStatesCallback {
+    method public void onResult(int, Throwable?);
+    field public static final int STATUS_EXCEEDED_BUFFER_ERROR = 3; // 0x3
+    field public static final int STATUS_SUCCESS = 0; // 0x0
+    field public static final int STATUS_TRANSACTION_TOO_LARGE_ERROR = 2; // 0x2
+    field public static final int STATUS_UNKNOWN_ERROR = 1; // 0x1
+  }
+
   public abstract class KeyedAppStatesReporter {
     method public static androidx.enterprise.feedback.KeyedAppStatesReporter create(android.content.Context);
     method public static androidx.enterprise.feedback.KeyedAppStatesReporter create(android.content.Context, java.util.concurrent.Executor);
-    method public abstract void setStates(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>);
-    method public abstract void setStatesImmediate(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>);
+    method @Deprecated public abstract void setStates(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>);
+    method public void setStates(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>, androidx.enterprise.feedback.KeyedAppStatesCallback?);
+    method @Deprecated public abstract void setStatesImmediate(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>);
+    method public void setStatesImmediate(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>, androidx.enterprise.feedback.KeyedAppStatesCallback?);
   }
 
   public abstract class KeyedAppStatesService extends android.app.Service {
diff --git a/enterprise/feedback/api/public_plus_experimental_1.1.0-alpha01.txt b/enterprise/feedback/api/public_plus_experimental_1.1.0-alpha01.txt
index 1cd14ea..4a23dc1 100644
--- a/enterprise/feedback/api/public_plus_experimental_1.1.0-alpha01.txt
+++ b/enterprise/feedback/api/public_plus_experimental_1.1.0-alpha01.txt
@@ -25,11 +25,21 @@
     method public abstract androidx.enterprise.feedback.KeyedAppState.KeyedAppStateBuilder setSeverity(int);
   }
 
+  public interface KeyedAppStatesCallback {
+    method public void onResult(int, Throwable?);
+    field public static final int STATUS_EXCEEDED_BUFFER_ERROR = 3; // 0x3
+    field public static final int STATUS_SUCCESS = 0; // 0x0
+    field public static final int STATUS_TRANSACTION_TOO_LARGE_ERROR = 2; // 0x2
+    field public static final int STATUS_UNKNOWN_ERROR = 1; // 0x1
+  }
+
   public abstract class KeyedAppStatesReporter {
     method public static androidx.enterprise.feedback.KeyedAppStatesReporter create(android.content.Context);
     method public static androidx.enterprise.feedback.KeyedAppStatesReporter create(android.content.Context, java.util.concurrent.Executor);
-    method public abstract void setStates(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>);
-    method public abstract void setStatesImmediate(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>);
+    method @Deprecated public abstract void setStates(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>);
+    method public void setStates(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>, androidx.enterprise.feedback.KeyedAppStatesCallback?);
+    method @Deprecated public abstract void setStatesImmediate(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>);
+    method public void setStatesImmediate(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>, androidx.enterprise.feedback.KeyedAppStatesCallback?);
   }
 
   public abstract class KeyedAppStatesService extends android.app.Service {
diff --git a/enterprise/feedback/api/public_plus_experimental_current.txt b/enterprise/feedback/api/public_plus_experimental_current.txt
index 1cd14ea..4a23dc1 100644
--- a/enterprise/feedback/api/public_plus_experimental_current.txt
+++ b/enterprise/feedback/api/public_plus_experimental_current.txt
@@ -25,11 +25,21 @@
     method public abstract androidx.enterprise.feedback.KeyedAppState.KeyedAppStateBuilder setSeverity(int);
   }
 
+  public interface KeyedAppStatesCallback {
+    method public void onResult(int, Throwable?);
+    field public static final int STATUS_EXCEEDED_BUFFER_ERROR = 3; // 0x3
+    field public static final int STATUS_SUCCESS = 0; // 0x0
+    field public static final int STATUS_TRANSACTION_TOO_LARGE_ERROR = 2; // 0x2
+    field public static final int STATUS_UNKNOWN_ERROR = 1; // 0x1
+  }
+
   public abstract class KeyedAppStatesReporter {
     method public static androidx.enterprise.feedback.KeyedAppStatesReporter create(android.content.Context);
     method public static androidx.enterprise.feedback.KeyedAppStatesReporter create(android.content.Context, java.util.concurrent.Executor);
-    method public abstract void setStates(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>);
-    method public abstract void setStatesImmediate(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>);
+    method @Deprecated public abstract void setStates(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>);
+    method public void setStates(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>, androidx.enterprise.feedback.KeyedAppStatesCallback?);
+    method @Deprecated public abstract void setStatesImmediate(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>);
+    method public void setStatesImmediate(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>, androidx.enterprise.feedback.KeyedAppStatesCallback?);
   }
 
   public abstract class KeyedAppStatesService extends android.app.Service {
diff --git a/enterprise/feedback/api/restricted_1.1.0-alpha01.txt b/enterprise/feedback/api/restricted_1.1.0-alpha01.txt
index 1cd14ea..4a23dc1 100644
--- a/enterprise/feedback/api/restricted_1.1.0-alpha01.txt
+++ b/enterprise/feedback/api/restricted_1.1.0-alpha01.txt
@@ -25,11 +25,21 @@
     method public abstract androidx.enterprise.feedback.KeyedAppState.KeyedAppStateBuilder setSeverity(int);
   }
 
+  public interface KeyedAppStatesCallback {
+    method public void onResult(int, Throwable?);
+    field public static final int STATUS_EXCEEDED_BUFFER_ERROR = 3; // 0x3
+    field public static final int STATUS_SUCCESS = 0; // 0x0
+    field public static final int STATUS_TRANSACTION_TOO_LARGE_ERROR = 2; // 0x2
+    field public static final int STATUS_UNKNOWN_ERROR = 1; // 0x1
+  }
+
   public abstract class KeyedAppStatesReporter {
     method public static androidx.enterprise.feedback.KeyedAppStatesReporter create(android.content.Context);
     method public static androidx.enterprise.feedback.KeyedAppStatesReporter create(android.content.Context, java.util.concurrent.Executor);
-    method public abstract void setStates(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>);
-    method public abstract void setStatesImmediate(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>);
+    method @Deprecated public abstract void setStates(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>);
+    method public void setStates(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>, androidx.enterprise.feedback.KeyedAppStatesCallback?);
+    method @Deprecated public abstract void setStatesImmediate(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>);
+    method public void setStatesImmediate(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>, androidx.enterprise.feedback.KeyedAppStatesCallback?);
   }
 
   public abstract class KeyedAppStatesService extends android.app.Service {
diff --git a/enterprise/feedback/api/restricted_current.txt b/enterprise/feedback/api/restricted_current.txt
index 1cd14ea..4a23dc1 100644
--- a/enterprise/feedback/api/restricted_current.txt
+++ b/enterprise/feedback/api/restricted_current.txt
@@ -25,11 +25,21 @@
     method public abstract androidx.enterprise.feedback.KeyedAppState.KeyedAppStateBuilder setSeverity(int);
   }
 
+  public interface KeyedAppStatesCallback {
+    method public void onResult(int, Throwable?);
+    field public static final int STATUS_EXCEEDED_BUFFER_ERROR = 3; // 0x3
+    field public static final int STATUS_SUCCESS = 0; // 0x0
+    field public static final int STATUS_TRANSACTION_TOO_LARGE_ERROR = 2; // 0x2
+    field public static final int STATUS_UNKNOWN_ERROR = 1; // 0x1
+  }
+
   public abstract class KeyedAppStatesReporter {
     method public static androidx.enterprise.feedback.KeyedAppStatesReporter create(android.content.Context);
     method public static androidx.enterprise.feedback.KeyedAppStatesReporter create(android.content.Context, java.util.concurrent.Executor);
-    method public abstract void setStates(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>);
-    method public abstract void setStatesImmediate(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>);
+    method @Deprecated public abstract void setStates(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>);
+    method public void setStates(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>, androidx.enterprise.feedback.KeyedAppStatesCallback?);
+    method @Deprecated public abstract void setStatesImmediate(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>);
+    method public void setStatesImmediate(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>, androidx.enterprise.feedback.KeyedAppStatesCallback?);
   }
 
   public abstract class KeyedAppStatesService extends android.app.Service {
diff --git a/enterprise/feedback/src/main/java/androidx/enterprise/feedback/BufferedServiceConnection.java b/enterprise/feedback/src/main/java/androidx/enterprise/feedback/BufferedServiceConnection.java
index f8d21aa..adc9ee3 100644
--- a/enterprise/feedback/src/main/java/androidx/enterprise/feedback/BufferedServiceConnection.java
+++ b/enterprise/feedback/src/main/java/androidx/enterprise/feedback/BufferedServiceConnection.java
@@ -16,6 +16,9 @@
 
 package androidx.enterprise.feedback;
 
+import static androidx.enterprise.feedback.KeyedAppStatesCallback.STATUS_EXCEEDED_BUFFER_ERROR;
+import static androidx.enterprise.feedback.KeyedAppStatesCallback.STATUS_TRANSACTION_TOO_LARGE_ERROR;
+import static androidx.enterprise.feedback.KeyedAppStatesCallback.STATUS_UNKNOWN_ERROR;
 import static androidx.enterprise.feedback.KeyedAppStatesReporter.canPackageReceiveAppStates;
 
 import android.content.ComponentName;
@@ -26,7 +29,7 @@
 import android.os.Message;
 import android.os.Messenger;
 import android.os.RemoteException;
-import android.util.Log;
+import android.os.TransactionTooLargeException;
 
 import androidx.annotation.VisibleForTesting;
 
@@ -45,8 +48,6 @@
  */
 class BufferedServiceConnection {
 
-    private static final String LOG_TAG = "BufferedServiceConnecti";
-
     @VisibleForTesting
     static final int MAX_BUFFER_SIZE = 100;
 
@@ -62,7 +63,7 @@
     boolean mIsDead = false;
     private boolean mHasBound = false;
     @SuppressWarnings("WeakerAccess") /* synthetic access */
-    final Queue<Message> mBuffer = new ArrayDeque<>();
+    final Queue<SendableMessage> mBuffer = new ArrayDeque<>();
     @SuppressWarnings("WeakerAccess") /* synthetic access */
     final Executor mExecutor;
 
@@ -119,6 +120,9 @@
                     mExecutor.execute(new Runnable() {
                         @Override
                         public void run() {
+                            // If this is now dead then the messages should not be sent, report
+                            // success
+                            reportSuccessOnBufferedMessages();
                             mIsDead = true;
                         }
                     });
@@ -136,6 +140,9 @@
                                 mMessenger = new Messenger(service);
                                 sendBufferedMessages();
                             } else {
+                                // If this is now dead then the messages should not be sent, report
+                                // success
+                                reportSuccessOnBufferedMessages();
                                 mIsDead = true;
                             }
                         }
@@ -149,6 +156,13 @@
                     }
                 }
 
+                @SuppressWarnings("WeakerAccess") /* synthetic access */
+                void reportSuccessOnBufferedMessages() {
+                    while (!mBuffer.isEmpty()) {
+                        mBuffer.poll().onSuccess();
+                    }
+                }
+
                 @Override
                 public void onServiceDisconnected(ComponentName componentName) {
                     mExecutor.execute(new Runnable() {
@@ -169,14 +183,17 @@
      * <p>The queue is capped at 100 messages. If 100 messages are already queued when send is
      * called and a connection is not established, the earliest message in the queue will be lost.
      */
-    void send(Message message) {
+    void send(SendableMessage message) {
         if (mIsDead) {
+            // Nothing will send on this connection, so we need to report success to allow it to
+            // resolve.
+            message.onSuccess();
             return;
         }
 
         if (mMessenger == null) {
             while (mBuffer.size() >= MAX_BUFFER_SIZE) {
-                mBuffer.poll();
+                mBuffer.poll().dealWithError(STATUS_EXCEEDED_BUFFER_ERROR, /* throwable= */ null);
             }
             mBuffer.add(message);
             return;
@@ -186,11 +203,14 @@
     }
 
     @SuppressWarnings("WeakerAccess") /* synthetic access */
-    void trySendMessage(Message message) {
+    void trySendMessage(SendableMessage message) {
         try {
-            mMessenger.send(message);
+            mMessenger.send(message.createStateMessage());
+            message.onSuccess();
+        } catch (TransactionTooLargeException e) {
+            message.dealWithError(STATUS_TRANSACTION_TOO_LARGE_ERROR, e);
         } catch (RemoteException e) {
-            Log.e(LOG_TAG, "Error sending message", e);
+            message.dealWithError(STATUS_UNKNOWN_ERROR, e);
         }
     }
 
diff --git a/enterprise/feedback/src/main/java/androidx/enterprise/feedback/DefaultKeyedAppStatesReporter.java b/enterprise/feedback/src/main/java/androidx/enterprise/feedback/DefaultKeyedAppStatesReporter.java
index 216a807..f771489 100644
--- a/enterprise/feedback/src/main/java/androidx/enterprise/feedback/DefaultKeyedAppStatesReporter.java
+++ b/enterprise/feedback/src/main/java/androidx/enterprise/feedback/DefaultKeyedAppStatesReporter.java
@@ -25,9 +25,9 @@
 import android.content.pm.ServiceInfo;
 import android.os.Build;
 import android.os.Bundle;
-import android.os.Message;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 
 import java.util.ArrayList;
 import java.util.Collection;
@@ -88,29 +88,48 @@
     }
 
     @Override
+    @Deprecated
     public void setStates(@NonNull Collection<KeyedAppState> states) {
-        setStates(states, false);
+        setStates(states, /* callback= */ null);
     }
 
-    private void setStates(final Collection<KeyedAppState> states, final boolean immediate) {
+    @Override
+    public void setStates(@NonNull Collection<KeyedAppState> states,
+            @Nullable KeyedAppStatesCallback callback) {
+        setStates(states, callback, /* immediate= */ false);
+    }
+
+    private void setStates(final Collection<KeyedAppState> states,
+            final KeyedAppStatesCallback callback, final boolean immediate) {
         mExecutor.execute(new Runnable() {
             @Override
             public void run() {
                 if (states.isEmpty()) {
+                    if (callback != null) {
+                        callback.onResult(
+                                KeyedAppStatesCallback.STATUS_SUCCESS, /* throwable= */ null);
+                    }
                     return;
                 }
 
                 unbindOldBindings();
                 bind();
 
-                send(buildStatesBundle(states), immediate);
+                send(buildStatesBundle(states), callback, immediate);
             }
         });
     }
 
     @Override
+    @Deprecated
     public void setStatesImmediate(@NonNull Collection<KeyedAppState> states) {
-        setStates(states, true);
+        setStatesImmediate(states, /* callback= */ null);
+    }
+
+    @Override
+    public void setStatesImmediate(@NonNull Collection<KeyedAppState> states,
+            @Nullable KeyedAppStatesCallback callback) {
+        setStates(states, callback, /* immediate= */ true);
     }
 
     @SuppressWarnings("WeakerAccess") /* synthetic access */
@@ -233,17 +252,14 @@
     }
 
     @SuppressWarnings("WeakerAccess") /* synthetic access */
-    void send(Bundle appStatesBundle, boolean immediate) {
-        for (BufferedServiceConnection serviceConnection : mServiceConnections.values()) {
-            // Messages cannot be reused so we create a copy for each service connection.
-            serviceConnection.send(createStateMessage(appStatesBundle, immediate));
+    void send(
+            Bundle appStatesBundle, @Nullable KeyedAppStatesCallback callback, boolean immediate) {
+        if (callback != null) {
+            // Callback will receive multiple callbacks so we need to merge them into a single one.
+            callback = new KeyedAppStatesCallbackMerger(mServiceConnections.size(), callback);
         }
-    }
-
-    private static Message createStateMessage(Bundle appStatesBundle, boolean immediate) {
-        Message message = Message.obtain();
-        message.what = immediate ? WHAT_IMMEDIATE_STATE : WHAT_STATE;
-        message.obj = appStatesBundle;
-        return message;
+        for (BufferedServiceConnection serviceConnection : mServiceConnections.values()) {
+            serviceConnection.send(new SendableMessage(appStatesBundle, callback, immediate));
+        }
     }
 }
diff --git a/enterprise/feedback/src/main/java/androidx/enterprise/feedback/KeyedAppStatesCallback.java b/enterprise/feedback/src/main/java/androidx/enterprise/feedback/KeyedAppStatesCallback.java
new file mode 100644
index 0000000..6030c7f
--- /dev/null
+++ b/enterprise/feedback/src/main/java/androidx/enterprise/feedback/KeyedAppStatesCallback.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2020 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.enterprise.feedback;
+
+import androidx.annotation.Nullable;
+
+import java.util.Collection;
+
+/**
+ * Interface used to listen for the result when using
+ * {@link KeyedAppStatesReporter#setStates(Collection, KeyedAppStatesCallback)} or
+ * {@link KeyedAppStatesReporter#setStatesImmediate(Collection, KeyedAppStatesCallback)}.
+ *
+ * <p>{@link #onResult(int, Throwable)} will only only report errors which occur inside this app.
+ * If a failure occurs in the Device Policy Controller then this will not be reported.
+ *
+ * <p>{@link #STATUS_SUCCESS} will be reported if the states are sent to all Device Policy
+ * Controllers.
+ */
+public interface KeyedAppStatesCallback {
+    /**
+     * Used when the states have been sent to all eligible receivers.
+     *
+     * <p>If there are 0 eligible receivers on the device, then this will be recorded as success.
+     */
+    int STATUS_SUCCESS = 0;
+
+    /** Used when an error has occurred which stopped the states being set that isn't covered by
+     * the other error types. */
+    int STATUS_UNKNOWN_ERROR = 1;
+
+    /** An error has occurred because the transaction setting the states has exceeded the Android
+     * binder limit (1MB). This can occur because the app is filling up the 1MB limit with other
+     * IPC calls, or because the size or number of states being set is too large.
+     */
+    int STATUS_TRANSACTION_TOO_LARGE_ERROR = 2;
+
+    /** An error occurred because the local app buffer was exceeded. This means too many setState
+     * or setStateImmediate calls have been made without a connection to the DPC being formed. */
+    int STATUS_EXCEEDED_BUFFER_ERROR = 3;
+
+    /**
+     * Called either when an error happens in this app, or when the states have been sent to all
+     * eligible receivers.
+     *
+     * <p>If there is an error, this will be called with the first error encountered.
+     *
+     * <p>If there are 0 eligible receivers on the device, then this will be recorded as success.
+     */
+    void onResult(int state, @Nullable Throwable throwable);
+}
diff --git a/enterprise/feedback/src/main/java/androidx/enterprise/feedback/KeyedAppStatesCallbackMerger.java b/enterprise/feedback/src/main/java/androidx/enterprise/feedback/KeyedAppStatesCallbackMerger.java
new file mode 100644
index 0000000..d63265d
--- /dev/null
+++ b/enterprise/feedback/src/main/java/androidx/enterprise/feedback/KeyedAppStatesCallbackMerger.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2020 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.enterprise.feedback;
+
+import androidx.annotation.Nullable;
+
+/**
+ * Merge multiple {@link KeyedAppStatesCallback} instances into a single one.
+ *
+ * <p>This will report success once {@code numReceivers} success results have been received.
+ *
+ * <p>It will report an error once a single non-success result is received.
+ */
+class KeyedAppStatesCallbackMerger implements KeyedAppStatesCallback {
+
+    private boolean mHasReported = false;
+    private int mSuccesses;
+    private final int mNumReceivers;
+    private final KeyedAppStatesCallback mOriginalCallback;
+
+    KeyedAppStatesCallbackMerger(int numReceivers, KeyedAppStatesCallback originalCallback) {
+        mNumReceivers = numReceivers;
+        mOriginalCallback = originalCallback;
+
+        if (mNumReceivers == 0) {
+            mHasReported = true;
+            mOriginalCallback.onResult(STATUS_SUCCESS, /* throwable= */ null);
+        }
+    }
+
+    @Override
+    public void onResult(int state, @Nullable Throwable throwable) {
+        if (mHasReported) {
+            // Only report once
+            return;
+        }
+
+        if (state != STATUS_SUCCESS || ++mSuccesses >= mNumReceivers) {
+            mHasReported = true;
+            mOriginalCallback.onResult(state, throwable);
+        }
+    }
+}
diff --git a/enterprise/feedback/src/main/java/androidx/enterprise/feedback/KeyedAppStatesReporter.java b/enterprise/feedback/src/main/java/androidx/enterprise/feedback/KeyedAppStatesReporter.java
index f46aa74..7f8de94 100644
--- a/enterprise/feedback/src/main/java/androidx/enterprise/feedback/KeyedAppStatesReporter.java
+++ b/enterprise/feedback/src/main/java/androidx/enterprise/feedback/KeyedAppStatesReporter.java
@@ -21,6 +21,7 @@
 import android.os.Message;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 
 import java.util.Collection;
 import java.util.concurrent.Executor;
@@ -116,6 +117,13 @@
     }
 
     /**
+     * @deprecated use {@link #setStates(Collection, KeyedAppStatesCallback)} which reports
+     * errors.
+     */
+    @Deprecated
+    public abstract void setStates(@NonNull Collection<KeyedAppState> states);
+
+    /**
      * Set app states to be sent to an EMM (enterprise mobility management). The EMM can then
      * display this information to the management organization.
      *
@@ -134,17 +142,38 @@
      * <p>EMMs can access these states either directly in a custom DPC (device policy manager), via
      * Android Management APIs, or via Play EMM APIs.
      *
-     * @see #setStatesImmediate(Collection)
+     * <p>{@link KeyedAppStatesCallback#onResult(int, Throwable)} will be called when an
+     * error occurs.
+     *
+     * @see #setStatesImmediate(Collection, KeyedAppStatesCallback)
      */
-    public abstract void setStates(@NonNull Collection<KeyedAppState> states);
+    public void setStates(@NonNull Collection<KeyedAppState> states,
+            @Nullable KeyedAppStatesCallback callback) {
+        throw new UnsupportedOperationException();
+    }
 
     /**
-     * Performs the same function as {@link #setStates(Collection)}, except it
-     * also requests that the states are immediately uploaded to be accessible
+     * @deprecated use {@link #setStatesImmediate(Collection, KeyedAppStatesCallback)} which
+     * reports errors.
+     */
+    @Deprecated
+    public abstract void setStatesImmediate(@NonNull Collection<KeyedAppState> states);
+
+    /**
+     * Performs the same function as {@link #setStates(Collection, KeyedAppStatesCallback)},
+     * except it also requests that the states are immediately uploaded to be accessible
      * via server APIs.
      *
      * <p>The receiver is not obligated to meet this immediate upload request.
      * For example, Play and Android Management APIs have daily quotas.
+     *
+     * <p>{@link KeyedAppStatesCallback#onResult(int, Throwable)} will be called
+     * when an error occurs.
+     *
+     * @see #setStates(Collection, KeyedAppStatesCallback)
      */
-    public abstract void setStatesImmediate(@NonNull Collection<KeyedAppState> states);
+    public void setStatesImmediate(@NonNull Collection<KeyedAppState> states,
+            @Nullable KeyedAppStatesCallback callback) {
+        throw new UnsupportedOperationException();
+    }
 }
diff --git a/enterprise/feedback/src/main/java/androidx/enterprise/feedback/SendableMessage.java b/enterprise/feedback/src/main/java/androidx/enterprise/feedback/SendableMessage.java
new file mode 100644
index 0000000..16fac5f
--- /dev/null
+++ b/enterprise/feedback/src/main/java/androidx/enterprise/feedback/SendableMessage.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2020 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.enterprise.feedback;
+
+import static androidx.enterprise.feedback.KeyedAppStatesReporter.WHAT_IMMEDIATE_STATE;
+import static androidx.enterprise.feedback.KeyedAppStatesReporter.WHAT_STATE;
+
+import android.os.Bundle;
+import android.os.Message;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+final class SendableMessage {
+    private static final String LOG_TAG = "SendableMessage";
+
+    private final Bundle mAppStatesBundle;
+    private final KeyedAppStatesCallback mCallback;
+    private final boolean mImmediate;
+
+    SendableMessage(@NonNull Bundle appStatesBundle, @Nullable KeyedAppStatesCallback callback,
+            boolean immediate) {
+        this.mAppStatesBundle = appStatesBundle;
+        this.mCallback = callback;
+        this.mImmediate = immediate;
+    }
+
+    @Nullable
+    KeyedAppStatesCallback getCallback() {
+        return mCallback;
+    }
+
+    Message createStateMessage() {
+        Message message = Message.obtain();
+        message.what = mImmediate ? WHAT_IMMEDIATE_STATE : WHAT_STATE;
+        message.obj = mAppStatesBundle;
+        return message;
+    }
+
+    void onSuccess() {
+        if (mCallback != null) {
+            mCallback.onResult(KeyedAppStatesCallback.STATUS_SUCCESS, /* throwable= */ null);
+        }
+    }
+
+    void dealWithError(int errorType, @Nullable Throwable throwable) {
+        if (mCallback != null) {
+            mCallback.onResult(errorType, throwable);
+        } else {
+            Log.e(LOG_TAG, "Error sending message. error: " + errorType, throwable);
+        }
+    }
+}
diff --git a/enterprise/feedback/src/test/java/androidx/enterprise/feedback/BufferedServiceConnectionTest.java b/enterprise/feedback/src/test/java/androidx/enterprise/feedback/BufferedServiceConnectionTest.java
index f802a4d..99f24dd 100644
--- a/enterprise/feedback/src/test/java/androidx/enterprise/feedback/BufferedServiceConnectionTest.java
+++ b/enterprise/feedback/src/test/java/androidx/enterprise/feedback/BufferedServiceConnectionTest.java
@@ -33,6 +33,7 @@
 import android.content.ContextWrapper;
 import android.content.Intent;
 import android.content.ServiceConnection;
+import android.os.Bundle;
 import android.os.IBinder;
 import android.os.Message;
 import android.os.Messenger;
@@ -77,6 +78,8 @@
     private final ComponentName mPhoneskyComponentName =
             new ComponentName("com.android.vending", "");
 
+    private final TestKeyedAppStatesCallback mCallback = new TestKeyedAppStatesCallback();
+
     @Before
     public void setUp() {
         setComponentBindingToTestHandler(mTestComponentName);
@@ -163,27 +166,50 @@
     @SmallTest
     public void sendMessage_bound_sends() {
         mBufferedServiceConnection.bindService();
+        shadowOf(getMainLooper()).idle();
+        SendableMessage sendableMessage = buildTestMessage();
 
-        mBufferedServiceConnection.send(buildTestMessage());
-
+        mBufferedServiceConnection.send(sendableMessage);
         shadowOf(getMainLooper()).idle();
 
-        // The test message is rebuilt as it is cleared after being sent
-        assertMessagesEqual(buildTestMessage(), mTestHandler.latestMessage());
+        assertMessagesEqual(sendableMessage.createStateMessage(), mTestHandler.latestMessage());
+    }
+
+    @Test
+    @SmallTest
+    public void sendMessage_bound_reportsSuccess() {
+        mBufferedServiceConnection.bindService();
+        SendableMessage sendableMessage = buildTestMessage(mCallback);
+
+        mBufferedServiceConnection.send(sendableMessage);
+        shadowOf(getMainLooper()).idle();
+
+        assertThat(mCallback.mTotalResults).isEqualTo(1);
+        assertThat(mCallback.mLatestState).isEqualTo(KeyedAppStatesCallback.STATUS_SUCCESS);
     }
 
     @Test
     @SmallTest
     public void sendMessage_notBound_doesNotSend() {
-        Message message = buildTestMessage();
+        SendableMessage sendableMessage = buildTestMessage();
 
-        mBufferedServiceConnection.send(message);
+        mBufferedServiceConnection.send(sendableMessage);
 
         assertThat(mTestHandler.latestMessage()).isNull();
     }
 
     @Test
     @SmallTest
+    public void sendMessage_notBound_doesNotCallback() {
+        SendableMessage sendableMessage = buildTestMessage(mCallback);
+
+        mBufferedServiceConnection.send(sendableMessage);
+
+        assertThat(mCallback.mTotalResults).isEqualTo(0);
+    }
+
+    @Test
+    @SmallTest
     @Config(minSdk = 26)
     public void sendMessage_isDead_doesNotSend() {
         mBufferedServiceConnection.bindService();
@@ -196,6 +222,18 @@
 
     @Test
     @SmallTest
+    @Config(minSdk = 26)
+    public void sendMessage_isDead_reportsSuccess() {
+        mBufferedServiceConnection.bindService();
+        simulateDeadServiceConnection();
+
+        mBufferedServiceConnection.send(buildTestMessage(mCallback));
+
+        assertThat(mCallback.mLatestState).isEqualTo(KeyedAppStatesCallback.STATUS_SUCCESS);
+    }
+
+    @Test
+    @SmallTest
     public void sendMessage_notBound_isNotDoPoOrPhonesky_doesNotSendWhenBound() {
         setComponentBindingToTestHandler(mNotPhoneskyComponentName);
         shadowOf(mDevicePolicyManager).setDeviceOwner(null);
@@ -203,12 +241,27 @@
         mBufferedServiceConnection.send(buildTestMessage());
 
         mBufferedServiceConnection.bindService();
+        shadowOf(getMainLooper()).idle();
 
         assertThat(mTestHandler.latestMessage()).isNull();
     }
 
     @Test
     @SmallTest
+    public void sendMessage_notBound_isNotDoPoOrPhonesky_reportsSuccessWhenBound() {
+        setComponentBindingToTestHandler(mNotPhoneskyComponentName);
+        shadowOf(mDevicePolicyManager).setDeviceOwner(null);
+        shadowOf(mDevicePolicyManager).setProfileOwner(null);
+        mBufferedServiceConnection.send(buildTestMessage(mCallback));
+
+        mBufferedServiceConnection.bindService();
+        shadowOf(getMainLooper()).idle();
+
+        assertThat(mCallback.mLatestState).isEqualTo(KeyedAppStatesCallback.STATUS_SUCCESS);
+    }
+
+    @Test
+    @SmallTest
     public void sendMessage_notBound_isNotDoPoOrPhonesky_isDeadWhenBound() {
         setComponentBindingToTestHandler(mNotPhoneskyComponentName);
         shadowOf(mDevicePolicyManager).setDeviceOwner(null);
@@ -216,7 +269,6 @@
         mBufferedServiceConnection.send(buildTestMessage());
 
         mBufferedServiceConnection.bindService();
-
         shadowOf(getMainLooper()).idle();
 
         assertThat(mBufferedServiceConnection.isDead()).isTrue();
@@ -226,42 +278,83 @@
     @SmallTest
     public void sendMessage_notBound_isDeviceOwner_sendsWhenBound() {
         shadowOf(mDevicePolicyManager).setDeviceOwner(mTestComponentName);
-        mBufferedServiceConnection.send(buildTestMessage());
+        SendableMessage sendableMessage = buildTestMessage();
+        mBufferedServiceConnection.send(sendableMessage);
 
         mBufferedServiceConnection.bindService();
-
         shadowOf(getMainLooper()).idle();
 
-        // The test message is rebuilt as it is cleared after being sent.
-        assertMessagesEqual(buildTestMessage(), mTestHandler.latestMessage());
+        assertMessagesEqual(sendableMessage.createStateMessage(), mTestHandler.latestMessage());
+    }
+
+    @Test
+    @SmallTest
+    public void sendMessage_notBound_isDeviceOwner_reportsSuccessWhenBound() {
+        shadowOf(mDevicePolicyManager).setDeviceOwner(mTestComponentName);
+        SendableMessage sendableMessage = buildTestMessage(mCallback);
+        mBufferedServiceConnection.send(sendableMessage);
+
+        mBufferedServiceConnection.bindService();
+        shadowOf(getMainLooper()).idle();
+
+        assertThat(mCallback.mTotalResults).isEqualTo(1);
+        assertThat(mCallback.mLatestState).isEqualTo(KeyedAppStatesCallback.STATUS_SUCCESS);
     }
 
     @Test
     @SmallTest
     public void sendMessage_notBound_isProfileOwner_sendsWhenBound() {
         shadowOf(mDevicePolicyManager).setProfileOwner(mTestComponentName);
-        mBufferedServiceConnection.send(buildTestMessage());
+        SendableMessage sendableMessage = buildTestMessage();
+        mBufferedServiceConnection.send(sendableMessage);
 
         mBufferedServiceConnection.bindService();
-
         shadowOf(getMainLooper()).idle();
 
         // The test message is rebuilt as it is cleared after being sent.
-        assertMessagesEqual(buildTestMessage(), mTestHandler.latestMessage());
+        assertMessagesEqual(sendableMessage.createStateMessage(), mTestHandler.latestMessage());
+    }
+
+    @Test
+    @SmallTest
+    public void sendMessage_notBound_isProfileOwner_reportsSuccessWhenBound() {
+        shadowOf(mDevicePolicyManager).setProfileOwner(mTestComponentName);
+        SendableMessage sendableMessage = buildTestMessage(mCallback);
+        mBufferedServiceConnection.send(sendableMessage);
+
+        mBufferedServiceConnection.bindService();
+        shadowOf(getMainLooper()).idle();
+
+        assertThat(mCallback.mTotalResults).isEqualTo(1);
+        assertThat(mCallback.mLatestState).isEqualTo(KeyedAppStatesCallback.STATUS_SUCCESS);
     }
 
     @Test
     @SmallTest
     public void sendMessage_notBound_isPhonesky_sendsWhenBound() {
         setComponentBindingToTestHandler(mPhoneskyComponentName);
-        mBufferedServiceConnection.send(buildTestMessage());
+        SendableMessage sendableMessage = buildTestMessage();
+        mBufferedServiceConnection.send(sendableMessage);
 
         mBufferedServiceConnection.bindService();
-
         shadowOf(getMainLooper()).idle();
 
         // The test message is rebuilt as it is cleared after being sent.
-        assertMessagesEqual(buildTestMessage(), mTestHandler.latestMessage());
+        assertMessagesEqual(sendableMessage.createStateMessage(), mTestHandler.latestMessage());
+    }
+
+    @Test
+    @SmallTest
+    public void sendMessage_notBound_isPhonesky_reportsSuccessWhenBound() {
+        setComponentBindingToTestHandler(mPhoneskyComponentName);
+        SendableMessage sendableMessage = buildTestMessage(mCallback);
+        mBufferedServiceConnection.send(sendableMessage);
+
+        mBufferedServiceConnection.bindService();
+        shadowOf(getMainLooper()).idle();
+
+        assertThat(mCallback.mTotalResults).isEqualTo(1);
+        assertThat(mCallback.mLatestState).isEqualTo(KeyedAppStatesCallback.STATUS_SUCCESS);
     }
 
     @Test
@@ -294,6 +387,20 @@
 
     @Test
     @SmallTest
+    public void sendMessage_notBound_sendBeyondBufferLimit_skippedMessagesReportError() {
+        mBufferedServiceConnection.send(buildTestMessage(mCallback));
+
+        for (int i = 0; i < MAX_BUFFER_SIZE; i++) {
+            mBufferedServiceConnection.send(buildTestMessage());
+        }
+
+        assertThat(mCallback.mTotalResults).isEqualTo(1);
+        assertThat(mCallback.mLatestState).isEqualTo(
+                KeyedAppStatesCallback.STATUS_EXCEEDED_BUFFER_ERROR);
+    }
+
+    @Test
+    @SmallTest
     public void isDead_isFalse() {
         mBufferedServiceConnection.bindService();
         assertThat(mBufferedServiceConnection.isDead()).isFalse();
@@ -357,11 +464,15 @@
                         service);
     }
 
-    private static Message buildTestMessage() {
-        Message message = Message.obtain();
-        message.arg1 = 100;
-        message.arg2 = 200;
-        return message;
+    private static SendableMessage buildTestMessage() {
+        return buildTestMessage(/* callback= */ null);
+    }
+
+    private static SendableMessage buildTestMessage(KeyedAppStatesCallback callback) {
+        Bundle bundle = new Bundle();
+        bundle.putInt("arg1", 100);
+        bundle.putInt("arg2", 200);
+        return new SendableMessage(bundle, /* callback= */ callback, /* immediate= */ false);
     }
 
     private static void assertMessagesEqual(Message expected, Message actual) {
diff --git a/enterprise/feedback/src/test/java/androidx/enterprise/feedback/DefaultKeyedAppStatesReporterTest.java b/enterprise/feedback/src/test/java/androidx/enterprise/feedback/DefaultKeyedAppStatesReporterTest.java
index 25d4590..76d2e9e 100644
--- a/enterprise/feedback/src/test/java/androidx/enterprise/feedback/DefaultKeyedAppStatesReporterTest.java
+++ b/enterprise/feedback/src/test/java/androidx/enterprise/feedback/DefaultKeyedAppStatesReporterTest.java
@@ -87,6 +87,7 @@
     private final KeyedAppState mState =
             KeyedAppState.builder().setKey("key").setSeverity(KeyedAppState.SEVERITY_INFO).build();
 
+    private final TestKeyedAppStatesCallback mCallback = new TestKeyedAppStatesCallback();
 
     @Test
     @SmallTest
@@ -126,7 +127,7 @@
         setTestHandlerReceivesStates();
 
         KeyedAppStatesReporter reporter = getReporter(mContext);
-        reporter.setStates(singletonList(mState));
+        reporter.setStates(singletonList(mState), /* callback= */ null);
         shadowOf(getMainLooper()).idle();
 
         Bundle appStatesBundle = buildStatesBundle(singleton(mState));
@@ -174,17 +175,41 @@
         setTestHandlerReceivesStates();
 
         KeyedAppStatesReporter reporter = getReporter(mContext);
-        reporter.setStates(Collections.<KeyedAppState>emptyList());
+        reporter.setStates(Collections.<KeyedAppState>emptyList(), /* callback= */ null);
 
         assertThat(mTestHandler.latestMessage()).isNull();
     }
 
     @Test
     @SmallTest
+    public void setEmpty_reportsSuccess() {
+        setTestHandlerReceivesStates();
+
+        KeyedAppStatesReporter reporter = getReporter(mContext);
+        reporter.setStates(Collections.<KeyedAppState>emptyList(), /* callback= */ mCallback);
+
+        assertThat(mCallback.mTotalResults).isEqualTo(1);
+        assertThat(mCallback.mLatestState).isEqualTo(KeyedAppStatesCallback.STATUS_SUCCESS);
+    }
+
+    @Test
+    @SmallTest
     public void setNotImmediate() {
         setTestHandlerReceivesStates();
 
         KeyedAppStatesReporter reporter = getReporter(mContext);
+        reporter.setStates(singletonList(mState), /* callback= */ null);
+        shadowOf(getMainLooper()).idle();
+
+        assertThat(mTestHandler.latestMessage().what).isEqualTo(WHAT_STATE);
+    }
+
+    @Test
+    @SmallTest
+    public void setNotImmediateDeprecated() {
+        setTestHandlerReceivesStates();
+
+        KeyedAppStatesReporter reporter = getReporter(mContext);
         reporter.setStates(singletonList(mState));
         shadowOf(getMainLooper()).idle();
 
@@ -193,10 +218,35 @@
 
     @Test
     @SmallTest
+    public void setNotImmediate_reportsSuccess() {
+        setTestHandlerReceivesStates();
+
+        KeyedAppStatesReporter reporter = getReporter(mContext);
+        reporter.setStates(singletonList(mState), /* callback= */ mCallback);
+        shadowOf(getMainLooper()).idle();
+
+        assertThat(mCallback.mTotalResults).isEqualTo(1);
+        assertThat(mCallback.mLatestState).isEqualTo(KeyedAppStatesCallback.STATUS_SUCCESS);
+    }
+
+    @Test
+    @SmallTest
     public void setImmediate() {
         setTestHandlerReceivesStates();
 
         KeyedAppStatesReporter reporter = getReporter(mContext);
+        reporter.setStatesImmediate(singletonList(mState), /* callback= */ null);
+        shadowOf(getMainLooper()).idle();
+
+        assertThat(mTestHandler.latestMessage().what).isEqualTo(WHAT_IMMEDIATE_STATE);
+    }
+
+    @Test
+    @SmallTest
+    public void setImmediateDeprecated() {
+        setTestHandlerReceivesStates();
+
+        KeyedAppStatesReporter reporter = getReporter(mContext);
         reporter.setStatesImmediate(singletonList(mState));
         shadowOf(getMainLooper()).idle();
 
@@ -205,12 +255,27 @@
 
     @Test
     @SmallTest
+    public void setImmediate_reportsSuccess() {
+        setTestHandlerReceivesStates();
+
+        KeyedAppStatesReporter reporter = getReporter(mContext);
+        reporter.setStatesImmediate(singletonList(mState), /* callback= */ mCallback);
+        shadowOf(getMainLooper()).idle();
+
+        assertThat(mCallback.mTotalResults).isEqualTo(1);
+        assertThat(mCallback.mLatestState).isEqualTo(KeyedAppStatesCallback.STATUS_SUCCESS);
+    }
+
+
+    @Test
+    @SmallTest
     public void set_doesNotGoToNormalApps() {
         addComponentAsRespondingToAppStatesIntent(mTestComponentName);
         setComponentBindingToHandler(mTestComponentName, mTestHandler);
 
         KeyedAppStatesReporter reporter = getReporter(mContext);
-        reporter.setStates(singletonList(mState));
+        reporter.setStates(singletonList(mState), /* callback= */ null);
+        shadowOf(getMainLooper()).idle();
 
         assertThat(mTestHandler.latestMessage()).isNull();
     }
@@ -223,7 +288,7 @@
         shadowOf(mDevicePolicyManager).setDeviceOwner(mTestComponentName);
 
         KeyedAppStatesReporter reporter = getReporter(mContext);
-        reporter.setStates(singletonList(mState));
+        reporter.setStates(singletonList(mState), /* callback= */ null);
         shadowOf(getMainLooper()).idle();
 
         assertThat(mTestHandler.latestMessage()).isNotNull();
@@ -237,7 +302,7 @@
         shadowOf(mDevicePolicyManager).setProfileOwner(mTestComponentName);
 
         KeyedAppStatesReporter reporter = getReporter(mContext);
-        reporter.setStates(singletonList(mState));
+        reporter.setStates(singletonList(mState), /* callback= */ null);
         shadowOf(getMainLooper()).idle();
 
         assertThat(mTestHandler.latestMessage()).isNotNull();
@@ -251,7 +316,7 @@
         setComponentBindingToHandler(phoneskyComponentName, mTestHandler);
 
         KeyedAppStatesReporter reporter = getReporter(mContext);
-        reporter.setStates(singletonList(mState));
+        reporter.setStates(singletonList(mState), /* callback= */ null);
         shadowOf(getMainLooper()).idle();
 
         assertThat(mTestHandler.latestMessage()).isNotNull();
@@ -272,10 +337,9 @@
 
         // Act
         KeyedAppStatesReporter reporter = getReporter(mContext);
-        reporter.setStates(singletonList(mState));
+        reporter.setStates(singletonList(mState), /* callback= */ null);
         shadowOf(getMainLooper()).idle();
 
-
         // Assert
         assertThat(mTestHandler.latestMessage()).isNotNull();
         assertThat(phoneskyTestHandler.latestMessage()).isNotNull();
@@ -283,13 +347,36 @@
 
     @Test
     @SmallTest
+    public void set_goesToMultiple_reportsSingleSuccess() {
+        // Arrange
+        addComponentAsRespondingToAppStatesIntent(mTestComponentName);
+        setComponentBindingToHandler(mTestComponentName, mTestHandler);
+        shadowOf(mDevicePolicyManager).setProfileOwner(mTestComponentName);
+
+        ComponentName phoneskyComponentName = new ComponentName(PHONESKY_PACKAGE_NAME, "");
+        TestHandler phoneskyTestHandler = new TestHandler();
+        addComponentAsRespondingToAppStatesIntent(phoneskyComponentName);
+        setComponentBindingToHandler(phoneskyComponentName, phoneskyTestHandler);
+
+        // Act
+        KeyedAppStatesReporter reporter = getReporter(mContext);
+        reporter.setStates(singletonList(mState), /* callback= */ mCallback);
+        shadowOf(getMainLooper()).idle();
+
+        // Assert
+        assertThat(mCallback.mTotalResults).isEqualTo(1);
+        assertThat(mCallback.mLatestState).isEqualTo(KeyedAppStatesCallback.STATUS_SUCCESS);
+    }
+
+    @Test
+    @SmallTest
     public void set_changeProfileOwner_goesToNewProfileOwner() {
         // Arrange
         addComponentAsRespondingToAppStatesIntent(mTestComponentName);
         setComponentBindingToHandler(mTestComponentName, mTestHandler);
         shadowOf(mDevicePolicyManager).setProfileOwner(mTestComponentName);
         KeyedAppStatesReporter reporter = getReporter(mContext);
-        reporter.setStates(singletonList(mState));
+        reporter.setStates(singletonList(mState), /* callback= */ null);
         mTestHandler.reset();
 
         ComponentName newComponentName = new ComponentName("second_test_package", "");
@@ -299,7 +386,7 @@
         shadowOf(mDevicePolicyManager).setProfileOwner(newComponentName);
 
         // Act
-        reporter.setStates(singletonList(mState));
+        reporter.setStates(singletonList(mState), /* callback= */ null);
         shadowOf(getMainLooper()).idle();
 
         // Assert
@@ -315,7 +402,7 @@
         setComponentBindingToHandler(mTestComponentName, mTestHandler);
         shadowOf(mDevicePolicyManager).setDeviceOwner(mTestComponentName);
         KeyedAppStatesReporter reporter = getReporter(mContext);
-        reporter.setStates(singletonList(mState));
+        reporter.setStates(singletonList(mState), /* callback= */ null);
         mTestHandler.reset();
 
         ComponentName newComponentName = new ComponentName("second_test_package", "");
@@ -325,7 +412,7 @@
         shadowOf(mDevicePolicyManager).setDeviceOwner(newComponentName);
 
         // Act
-        reporter.setStates(singletonList(mState));
+        reporter.setStates(singletonList(mState), /* callback= */ null);
         shadowOf(getMainLooper()).idle();
 
         // Assert
@@ -343,7 +430,7 @@
         shadowOf(mDevicePolicyManager).setProfileOwner(mTestComponentName);
 
         KeyedAppStatesReporter reporter = getReporter(mContext);
-        reporter.setStates(singletonList(mState));
+        reporter.setStates(singletonList(mState), /* callback= */ null);
         shadowOf(getMainLooper()).idle();
         mTestHandler.reset();
 
@@ -354,7 +441,7 @@
         simulateDeadServiceConnection();
 
         // Act
-        reporter.setStates(singletonList(mState));
+        reporter.setStates(singletonList(mState), /* callback= */ null);
         shadowOf(getMainLooper()).idle();
 
         // Assert
@@ -372,7 +459,7 @@
         shadowOf(mDevicePolicyManager).setProfileOwner(mTestComponentName);
 
         KeyedAppStatesReporter reporter = getReporter(mContext);
-        reporter.setStates(singletonList(mState));
+        reporter.setStates(singletonList(mState), /* callback= */ null);
         shadowOf(getMainLooper()).idle();
         mTestHandler.reset();
 
@@ -383,7 +470,7 @@
         simulateDisconnectingServiceConnection();
 
         // Act
-        reporter.setStates(singletonList(mState));
+        reporter.setStates(singletonList(mState), /* callback= */ null);
         shadowOf(getMainLooper()).idle();
 
         // Assert
@@ -401,14 +488,14 @@
         shadowOf(mDevicePolicyManager).setProfileOwner(mTestComponentName);
 
         KeyedAppStatesReporter reporter = getReporter(mContext);
-        reporter.setStates(singletonList(mState));
+        reporter.setStates(singletonList(mState), /* callback= */ null);
         shadowOf(getMainLooper()).idle();
         mTestHandler.reset();
 
         simulateDisconnectingServiceConnection();
 
         // Act
-        reporter.setStates(singletonList(mState));
+        reporter.setStates(singletonList(mState), /* callback= */ null);
         shadowOf(getMainLooper()).idle();
 
         // Assert
@@ -418,6 +505,28 @@
     @Test
     @SmallTest
     @Config(minSdk = 26)
+    public void set_connectionHasDisconnected_doesNotCallback() {
+        // Arrange
+        addComponentAsRespondingToAppStatesIntent(mTestComponentName);
+        setComponentBindingToHandler(mTestComponentName, mTestHandler);
+        shadowOf(mDevicePolicyManager).setProfileOwner(mTestComponentName);
+
+        KeyedAppStatesReporter reporter = getReporter(mContext);
+        reporter.setStates(singletonList(mState), /* callback= */ null);
+        mTestHandler.reset();
+
+        simulateDisconnectingServiceConnection();
+
+        // Act
+        reporter.setStates(singletonList(mState), /* callback= */ mCallback);
+
+        // Assert
+        assertThat(mCallback.mTotalResults).isEqualTo(0);
+    }
+
+    @Test
+    @SmallTest
+    @Config(minSdk = 26)
     public void set_sendsWhenReconnected() {
         // Arrange
         addComponentAsRespondingToAppStatesIntent(mTestComponentName);
@@ -425,11 +534,11 @@
         shadowOf(mDevicePolicyManager).setProfileOwner(mTestComponentName);
 
         KeyedAppStatesReporter reporter = getReporter(mContext);
-        reporter.setStates(singletonList(mState));
+        reporter.setStates(singletonList(mState), /* callback= */ null);
         mTestHandler.reset();
 
         simulateDisconnectingServiceConnection();
-        reporter.setStates(singletonList(mState));
+        reporter.setStates(singletonList(mState), /* callback= */ null);
 
         // Act
         simulateReconnectingServiceConnection();
@@ -441,6 +550,30 @@
 
     @Test
     @SmallTest
+    @Config(minSdk = 26)
+    public void set_reportsSuccessWhenReconnected() {
+        // Arrange
+        addComponentAsRespondingToAppStatesIntent(mTestComponentName);
+        setComponentBindingToHandler(mTestComponentName, mTestHandler);
+        shadowOf(mDevicePolicyManager).setProfileOwner(mTestComponentName);
+
+        KeyedAppStatesReporter reporter = getReporter(mContext);
+        reporter.setStates(singletonList(mState), /* callback= */ null);
+        mTestHandler.reset();
+
+        simulateDisconnectingServiceConnection();
+        reporter.setStates(singletonList(mState), /* callback= */ mCallback);
+
+        // Act
+        simulateReconnectingServiceConnection();
+
+        // Assert
+        assertThat(mCallback.mTotalResults).isEqualTo(1);
+        assertThat(mCallback.mLatestState).isEqualTo(KeyedAppStatesCallback.STATUS_SUCCESS);
+    }
+
+    @Test
+    @SmallTest
     public void set_connectionHasReconnected_doesSend() {
         // Arrange
         addComponentAsRespondingToAppStatesIntent(mTestComponentName);
@@ -448,7 +581,7 @@
         shadowOf(mDevicePolicyManager).setProfileOwner(mTestComponentName);
 
         KeyedAppStatesReporter reporter = getReporter(mContext);
-        reporter.setStates(singletonList(mState));
+        reporter.setStates(singletonList(mState), /* callback= */ null);
         mTestHandler.reset();
 
         // Change the component binding to ensure that it doesn't reconnect
@@ -458,7 +591,7 @@
         simulateReconnectingServiceConnection();
 
         // Act
-        reporter.setStates(singletonList(mState));
+        reporter.setStates(singletonList(mState), /* callback= */ null);
         shadowOf(getMainLooper()).idle();
 
         // Assert
@@ -511,4 +644,26 @@
     private KeyedAppStatesReporter getReporter(Context context) {
         return new DefaultKeyedAppStatesReporter(context, mExecutor);
     }
+
+    private static Collection<KeyedAppState> generateMaximumSizeStates() {
+        Collection<KeyedAppState> states = new ArrayList<>();
+        for (int i = 0; i < 500; i++) {
+            states.add(generateLargeState("key" + i));
+        }
+        return states;
+    }
+
+    private static KeyedAppState generateLargeState(String keySuffix) {
+        return KeyedAppState.builder()
+                .setKey(generateStringOfLength(
+                        KeyedAppState.MAX_KEY_LENGTH - keySuffix.length()) + keySuffix)
+                .setSeverity(KeyedAppState.SEVERITY_INFO)
+                .setData(generateStringOfLength(KeyedAppState.MAX_DATA_LENGTH))
+                .setMessage(generateStringOfLength(KeyedAppState.MAX_MESSAGE_LENGTH))
+                .build();
+    }
+
+    private static String generateStringOfLength(int length) {
+        return String.format("%0" + length + "d", 0);
+    }
 }
diff --git a/enterprise/feedback/src/test/java/androidx/enterprise/feedback/KeyedAppStatesCallbackMergerTest.java b/enterprise/feedback/src/test/java/androidx/enterprise/feedback/KeyedAppStatesCallbackMergerTest.java
new file mode 100644
index 0000000..e6305c7
--- /dev/null
+++ b/enterprise/feedback/src/test/java/androidx/enterprise/feedback/KeyedAppStatesCallbackMergerTest.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2020 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.enterprise.feedback;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+import org.robolectric.annotation.internal.DoNotInstrument;
+
+/** Tests {@link KeyedAppStatesCallbackMerger}. */
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
+@Config(minSdk = 21)
+public class KeyedAppStatesCallbackMergerTest {
+
+    private final TestKeyedAppStatesCallback mCallback = new TestKeyedAppStatesCallback();
+    private final Throwable mTestThrowable = new IllegalArgumentException();
+
+    @Test
+    @SmallTest
+    public void notZeroExpected_noImmediateCallback() {
+        new KeyedAppStatesCallbackMerger(1, mCallback);
+
+        assertThat(mCallback.mTotalResults).isEqualTo(0);
+    }
+
+    @Test
+    @SmallTest
+    public void zeroExpected_successCallbackImmediately() {
+        new KeyedAppStatesCallbackMerger(0, mCallback);
+
+        assertThat(mCallback.mTotalResults).isEqualTo(1);
+        assertThat(mCallback.mLatestState).isEqualTo(KeyedAppStatesCallback.STATUS_SUCCESS);
+    }
+
+    @Test
+    @SmallTest
+    public void oneExpected_successCallbackIsPassedThrough() {
+        KeyedAppStatesCallbackMerger merger =
+                new KeyedAppStatesCallbackMerger(1, mCallback);
+
+        merger.onResult(KeyedAppStatesCallback.STATUS_SUCCESS, /* throwable= */ null);
+
+        assertThat(mCallback.mTotalResults).isEqualTo(1);
+        assertThat(mCallback.mLatestState).isEqualTo(KeyedAppStatesCallback.STATUS_SUCCESS);
+    }
+
+    @Test
+    @SmallTest
+    public void twoExpected_firstSuccessCallbackIsNotPassedThrough() {
+        KeyedAppStatesCallbackMerger merger =
+                new KeyedAppStatesCallbackMerger(2, mCallback);
+
+        merger.onResult(KeyedAppStatesCallback.STATUS_SUCCESS, /* throwable= */ null);
+
+        assertThat(mCallback.mTotalResults).isEqualTo(0);
+    }
+
+    @Test
+    @SmallTest
+    public void twoExpected_secondSuccessCallbackIsPassedThrough() {
+        KeyedAppStatesCallbackMerger merger =
+                new KeyedAppStatesCallbackMerger(2, mCallback);
+        merger.onResult(KeyedAppStatesCallback.STATUS_SUCCESS, /* throwable= */ null);
+
+        merger.onResult(KeyedAppStatesCallback.STATUS_SUCCESS, /* throwable= */ null);
+
+        assertThat(mCallback.mTotalResults).isEqualTo(1);
+        assertThat(mCallback.mLatestState).isEqualTo(KeyedAppStatesCallback.STATUS_SUCCESS);
+    }
+
+    @Test
+    @SmallTest
+    public void twoExpected_thirdSuccessCallbackIsNotPassedThrough() {
+        KeyedAppStatesCallbackMerger merger =
+                new KeyedAppStatesCallbackMerger(2, mCallback);
+        merger.onResult(KeyedAppStatesCallback.STATUS_SUCCESS, /* throwable= */ null);
+        merger.onResult(KeyedAppStatesCallback.STATUS_SUCCESS, /* throwable= */ null);
+
+        merger.onResult(KeyedAppStatesCallback.STATUS_SUCCESS, /* throwable= */ null);
+
+        assertThat(mCallback.mTotalResults).isEqualTo(1);
+    }
+
+    @Test
+    @SmallTest
+    public void oneExpected_failureCallbackIsPassedThrough() {
+        KeyedAppStatesCallbackMerger merger =
+                new KeyedAppStatesCallbackMerger(1, mCallback);
+
+        merger.onResult(
+                KeyedAppStatesCallback.STATUS_TRANSACTION_TOO_LARGE_ERROR, /* throwable= */ null);
+
+        assertThat(mCallback.mTotalResults).isEqualTo(1);
+        assertThat(mCallback.mLatestState).isEqualTo(
+                KeyedAppStatesCallback.STATUS_TRANSACTION_TOO_LARGE_ERROR);
+    }
+
+    @Test
+    @SmallTest
+    public void twoExpected_firstFailureCallbackIsPassedThrough() {
+        KeyedAppStatesCallbackMerger merger =
+                new KeyedAppStatesCallbackMerger(2, mCallback);
+
+        merger.onResult(
+                KeyedAppStatesCallback.STATUS_TRANSACTION_TOO_LARGE_ERROR, /* throwable= */ null);
+
+        assertThat(mCallback.mTotalResults).isEqualTo(1);
+        assertThat(mCallback.mLatestState).isEqualTo(
+                KeyedAppStatesCallback.STATUS_TRANSACTION_TOO_LARGE_ERROR);
+    }
+
+    @Test
+    @SmallTest
+    public void twoExpected_secondFailureCallbackIsNotPassedThrough() {
+        KeyedAppStatesCallbackMerger merger =
+                new KeyedAppStatesCallbackMerger(2, mCallback);
+        merger.onResult(
+                KeyedAppStatesCallback.STATUS_TRANSACTION_TOO_LARGE_ERROR, /* throwable= */ null);
+
+        merger.onResult(
+                KeyedAppStatesCallback.STATUS_TRANSACTION_TOO_LARGE_ERROR, /* throwable= */ null);
+
+        assertThat(mCallback.mTotalResults).isEqualTo(1);
+        assertThat(mCallback.mLatestState).isEqualTo(
+                KeyedAppStatesCallback.STATUS_TRANSACTION_TOO_LARGE_ERROR);
+    }
+
+    @Test
+    @SmallTest
+    public void twoExpected_alreadyFailed_laterSuccessCallbacksAreNotPassedThrough() {
+        KeyedAppStatesCallbackMerger merger =
+                new KeyedAppStatesCallbackMerger(2, mCallback);
+        merger.onResult(
+                KeyedAppStatesCallback.STATUS_TRANSACTION_TOO_LARGE_ERROR, /* throwable= */ null);
+
+        merger.onResult(KeyedAppStatesCallback.STATUS_SUCCESS, /* throwable= */ null);
+        merger.onResult(KeyedAppStatesCallback.STATUS_SUCCESS, /* throwable= */ null);
+
+        assertThat(mCallback.mTotalResults).isEqualTo(1);
+        assertThat(mCallback.mLatestState).isEqualTo(
+                KeyedAppStatesCallback.STATUS_TRANSACTION_TOO_LARGE_ERROR);
+    }
+
+    @Test
+    @SmallTest
+    public void throwableIsPassedThrough() {
+        KeyedAppStatesCallbackMerger merger =
+                new KeyedAppStatesCallbackMerger(1, mCallback);
+
+        merger.onResult(
+                KeyedAppStatesCallback.STATUS_TRANSACTION_TOO_LARGE_ERROR,
+                /* throwable= */ mTestThrowable);
+
+        assertThat(mCallback.mLatestThrowable).isEqualTo(mTestThrowable);
+    }
+}
diff --git a/enterprise/feedback/src/test/java/androidx/enterprise/feedback/KeyedAppStatesReporterTest.java b/enterprise/feedback/src/test/java/androidx/enterprise/feedback/KeyedAppStatesReporterTest.java
index 936dc29..1b83245 100644
--- a/enterprise/feedback/src/test/java/androidx/enterprise/feedback/KeyedAppStatesReporterTest.java
+++ b/enterprise/feedback/src/test/java/androidx/enterprise/feedback/KeyedAppStatesReporterTest.java
@@ -100,7 +100,7 @@
         KeyedAppStatesReporter reporter =
                 KeyedAppStatesReporter.create(mContext, testExecutor);
 
-        reporter.setStates(singleton(mState));
+        reporter.setStates(singleton(mState), /* callback= */ null);
 
         assertThat(testExecutor.lastExecuted()).isNotNull();
     }
diff --git a/enterprise/feedback/src/test/java/androidx/enterprise/feedback/TestKeyedAppStatesCallback.java b/enterprise/feedback/src/test/java/androidx/enterprise/feedback/TestKeyedAppStatesCallback.java
new file mode 100644
index 0000000..37e05c9
--- /dev/null
+++ b/enterprise/feedback/src/test/java/androidx/enterprise/feedback/TestKeyedAppStatesCallback.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2020 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.enterprise.feedback;
+
+import androidx.annotation.Nullable;
+
+class TestKeyedAppStatesCallback implements KeyedAppStatesCallback {
+
+    int mTotalResults = 0;
+    int mLatestState = -1;
+    Throwable mLatestThrowable = null;
+
+    @Override
+    public void onResult(int state, @Nullable Throwable throwable) {
+        mTotalResults++;
+        mLatestState = state;
+        mLatestThrowable = throwable;
+    }
+}
diff --git a/enterprise/feedback/testing/api/1.1.0-alpha01.txt b/enterprise/feedback/testing/api/1.1.0-alpha01.txt
index beb926b..89ff8fd 100644
--- a/enterprise/feedback/testing/api/1.1.0-alpha01.txt
+++ b/enterprise/feedback/testing/api/1.1.0-alpha01.txt
@@ -10,8 +10,8 @@
     method public java.util.Map<java.lang.String!,androidx.enterprise.feedback.KeyedAppState!> getOnDeviceKeyedAppStatesByKey();
     method public java.util.List<androidx.enterprise.feedback.KeyedAppState!> getUploadedKeyedAppStates();
     method public java.util.Map<java.lang.String!,androidx.enterprise.feedback.KeyedAppState!> getUploadedKeyedAppStatesByKey();
-    method public void setStates(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>);
-    method public void setStatesImmediate(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>);
+    method @Deprecated public void setStates(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>);
+    method @Deprecated public void setStatesImmediate(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>);
   }
 
 }
diff --git a/enterprise/feedback/testing/api/current.txt b/enterprise/feedback/testing/api/current.txt
index beb926b..89ff8fd 100644
--- a/enterprise/feedback/testing/api/current.txt
+++ b/enterprise/feedback/testing/api/current.txt
@@ -10,8 +10,8 @@
     method public java.util.Map<java.lang.String!,androidx.enterprise.feedback.KeyedAppState!> getOnDeviceKeyedAppStatesByKey();
     method public java.util.List<androidx.enterprise.feedback.KeyedAppState!> getUploadedKeyedAppStates();
     method public java.util.Map<java.lang.String!,androidx.enterprise.feedback.KeyedAppState!> getUploadedKeyedAppStatesByKey();
-    method public void setStates(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>);
-    method public void setStatesImmediate(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>);
+    method @Deprecated public void setStates(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>);
+    method @Deprecated public void setStatesImmediate(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>);
   }
 
 }
diff --git a/enterprise/feedback/testing/api/public_plus_experimental_1.1.0-alpha01.txt b/enterprise/feedback/testing/api/public_plus_experimental_1.1.0-alpha01.txt
index beb926b..89ff8fd 100644
--- a/enterprise/feedback/testing/api/public_plus_experimental_1.1.0-alpha01.txt
+++ b/enterprise/feedback/testing/api/public_plus_experimental_1.1.0-alpha01.txt
@@ -10,8 +10,8 @@
     method public java.util.Map<java.lang.String!,androidx.enterprise.feedback.KeyedAppState!> getOnDeviceKeyedAppStatesByKey();
     method public java.util.List<androidx.enterprise.feedback.KeyedAppState!> getUploadedKeyedAppStates();
     method public java.util.Map<java.lang.String!,androidx.enterprise.feedback.KeyedAppState!> getUploadedKeyedAppStatesByKey();
-    method public void setStates(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>);
-    method public void setStatesImmediate(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>);
+    method @Deprecated public void setStates(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>);
+    method @Deprecated public void setStatesImmediate(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>);
   }
 
 }
diff --git a/enterprise/feedback/testing/api/public_plus_experimental_current.txt b/enterprise/feedback/testing/api/public_plus_experimental_current.txt
index beb926b..89ff8fd 100644
--- a/enterprise/feedback/testing/api/public_plus_experimental_current.txt
+++ b/enterprise/feedback/testing/api/public_plus_experimental_current.txt
@@ -10,8 +10,8 @@
     method public java.util.Map<java.lang.String!,androidx.enterprise.feedback.KeyedAppState!> getOnDeviceKeyedAppStatesByKey();
     method public java.util.List<androidx.enterprise.feedback.KeyedAppState!> getUploadedKeyedAppStates();
     method public java.util.Map<java.lang.String!,androidx.enterprise.feedback.KeyedAppState!> getUploadedKeyedAppStatesByKey();
-    method public void setStates(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>);
-    method public void setStatesImmediate(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>);
+    method @Deprecated public void setStates(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>);
+    method @Deprecated public void setStatesImmediate(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>);
   }
 
 }
diff --git a/enterprise/feedback/testing/api/restricted_1.1.0-alpha01.txt b/enterprise/feedback/testing/api/restricted_1.1.0-alpha01.txt
index beb926b..89ff8fd 100644
--- a/enterprise/feedback/testing/api/restricted_1.1.0-alpha01.txt
+++ b/enterprise/feedback/testing/api/restricted_1.1.0-alpha01.txt
@@ -10,8 +10,8 @@
     method public java.util.Map<java.lang.String!,androidx.enterprise.feedback.KeyedAppState!> getOnDeviceKeyedAppStatesByKey();
     method public java.util.List<androidx.enterprise.feedback.KeyedAppState!> getUploadedKeyedAppStates();
     method public java.util.Map<java.lang.String!,androidx.enterprise.feedback.KeyedAppState!> getUploadedKeyedAppStatesByKey();
-    method public void setStates(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>);
-    method public void setStatesImmediate(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>);
+    method @Deprecated public void setStates(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>);
+    method @Deprecated public void setStatesImmediate(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>);
   }
 
 }
diff --git a/enterprise/feedback/testing/api/restricted_current.txt b/enterprise/feedback/testing/api/restricted_current.txt
index beb926b..89ff8fd 100644
--- a/enterprise/feedback/testing/api/restricted_current.txt
+++ b/enterprise/feedback/testing/api/restricted_current.txt
@@ -10,8 +10,8 @@
     method public java.util.Map<java.lang.String!,androidx.enterprise.feedback.KeyedAppState!> getOnDeviceKeyedAppStatesByKey();
     method public java.util.List<androidx.enterprise.feedback.KeyedAppState!> getUploadedKeyedAppStates();
     method public java.util.Map<java.lang.String!,androidx.enterprise.feedback.KeyedAppState!> getUploadedKeyedAppStatesByKey();
-    method public void setStates(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>);
-    method public void setStatesImmediate(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>);
+    method @Deprecated public void setStates(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>);
+    method @Deprecated public void setStatesImmediate(java.util.Collection<androidx.enterprise.feedback.KeyedAppState!>);
   }
 
 }
diff --git a/enterprise/feedback/testing/src/main/java/androidx/enterprise/feedback/FakeKeyedAppStatesReporter.java b/enterprise/feedback/testing/src/main/java/androidx/enterprise/feedback/FakeKeyedAppStatesReporter.java
index 8f9d908..9b221bd 100644
--- a/enterprise/feedback/testing/src/main/java/androidx/enterprise/feedback/FakeKeyedAppStatesReporter.java
+++ b/enterprise/feedback/testing/src/main/java/androidx/enterprise/feedback/FakeKeyedAppStatesReporter.java
@@ -17,6 +17,7 @@
 package androidx.enterprise.feedback;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 
 import java.util.ArrayList;
 import java.util.Collection;
@@ -54,24 +55,48 @@
             Collections.synchronizedMap(new HashMap<String, KeyedAppState>());
     private AtomicInteger mNumberOfUploads = new AtomicInteger();
 
+    /** @deprecated see {@link #setStates(Collection, KeyedAppStatesCallback)}. **/
     @Override
+    @Deprecated
     public void setStates(@NonNull Collection<KeyedAppState> states) {
+        setStates(states, /* callback= */ null);
+    }
+
+    /**
+     * Record the states set.
+     *
+     * <p>Does not enforce any limit on total size of states collection.
+     */
+    @Override
+    public void setStates(@NonNull Collection<KeyedAppState> states,
+            @Nullable KeyedAppStatesCallback callback) {
         for (KeyedAppState state : states) {
             mOnDeviceKeyedAppStates.add(state);
             mOnDeviceKeyedAppStatesByKey.put(state.getKey(), state);
             mKeyedAppStates.add(state);
             mKeyedAppStatesByKey.put(state.getKey(), state);
         }
+        if (callback != null) {
+            callback.onResult(KeyedAppStatesCallback.STATUS_SUCCESS, /* throwable= */ null);
+        }
+    }
+
+    /** @deprecated See {@link #setStatesImmediate(Collection, KeyedAppStatesCallback)}. **/
+    @Override
+    @Deprecated
+    public void setStatesImmediate(@NonNull Collection<KeyedAppState> states) {
+        setStatesImmediate(states, /* callback= */ null);
     }
 
     /**
      * Record the set states and immediately mark all states as having been uploaded.
      *
-     * <p>Does not enforce any quota on uploading.
+     * <p>Does not enforce any quota on uploading, or limit on total size of states collection.
      */
     @Override
-    public void setStatesImmediate(@NonNull Collection<KeyedAppState> states) {
-        setStates(states);
+    public void setStatesImmediate(@NonNull Collection<KeyedAppState> states,
+            @Nullable KeyedAppStatesCallback callback) {
+        setStates(states, callback);
         upload();
     }
 
diff --git a/enterprise/feedback/testing/src/test/java/androidx/enterprise/feedback/FakeKeyedAppStatesReporterTest.java b/enterprise/feedback/testing/src/test/java/androidx/enterprise/feedback/FakeKeyedAppStatesReporterTest.java
index be7ecdd..fe34f6a 100644
--- a/enterprise/feedback/testing/src/test/java/androidx/enterprise/feedback/FakeKeyedAppStatesReporterTest.java
+++ b/enterprise/feedback/testing/src/test/java/androidx/enterprise/feedback/FakeKeyedAppStatesReporterTest.java
@@ -50,6 +50,8 @@
             .setMessage("different-message")
             .build();
 
+    private final TestKeyedAppStatesCallback mCallback = new TestKeyedAppStatesCallback();
+
     @Test
     public void beginsEmpty() {
         FakeKeyedAppStatesReporter reporter = new FakeKeyedAppStatesReporter();
@@ -58,7 +60,30 @@
     }
 
     @Test
+    public void setStates_reportsSuccess() {
+        mReporter.setStates(singletonList(KEYED_APP_STATE), mCallback);
+
+        assertThat(mCallback.mTotalResults).isEqualTo(1);
+        assertThat(mCallback.mLatestState).isEqualTo(KeyedAppStatesCallback.STATUS_SUCCESS);
+    }
+
+    @Test
+    public void setStatesImmediate_reportsSuccess() {
+        mReporter.setStatesImmediate(singletonList(KEYED_APP_STATE), mCallback);
+
+        assertThat(mCallback.mTotalResults).isEqualTo(1);
+        assertThat(mCallback.mLatestState).isEqualTo(KeyedAppStatesCallback.STATUS_SUCCESS);
+    }
+
+    @Test
     public void setStates_single_isRecordedInOnDeviceKeyedAppStates() {
+        mReporter.setStates(singletonList(KEYED_APP_STATE), /* callback= */ null);
+
+        assertThat(mReporter.getOnDeviceKeyedAppStates()).containsExactly(KEYED_APP_STATE);
+    }
+
+    @Test
+    public void setStates_deprecated_isRecordedInOnDeviceKeyedAppStates() {
         mReporter.setStates(singletonList(KEYED_APP_STATE));
 
         assertThat(mReporter.getOnDeviceKeyedAppStates()).containsExactly(KEYED_APP_STATE);
@@ -66,7 +91,7 @@
 
     @Test
     public void setStates_single_isRecordedInOnDeviceKeyedAppStatesByKey() {
-        mReporter.setStates(singletonList(KEYED_APP_STATE));
+        mReporter.setStates(singletonList(KEYED_APP_STATE), /* callback= */ null);
 
         assertThat(mReporter.getOnDeviceKeyedAppStatesByKey().values())
                 .containsExactly(KEYED_APP_STATE);
@@ -76,7 +101,8 @@
 
     @Test
     public void setStates_multiple_isRecordedInOnDeviceKeyedAppStates() {
-        mReporter.setStates(asList(KEYED_APP_STATE, KEYED_APP_STATE_DIFFERENT_KEY));
+        mReporter.setStates(
+                asList(KEYED_APP_STATE, KEYED_APP_STATE_DIFFERENT_KEY), /* callback= */ null);
 
         assertThat(mReporter.getOnDeviceKeyedAppStates())
                 .containsExactly(KEYED_APP_STATE, KEYED_APP_STATE_DIFFERENT_KEY);
@@ -84,7 +110,8 @@
 
     @Test
     public void setStates_multiple_isRecordedInOnDeviceKeyedAppStatesByKey() {
-        mReporter.setStates(asList(KEYED_APP_STATE, KEYED_APP_STATE_DIFFERENT_KEY));
+        mReporter.setStates(
+                asList(KEYED_APP_STATE, KEYED_APP_STATE_DIFFERENT_KEY), /* callback= */ null);
 
         assertThat(mReporter.getOnDeviceKeyedAppStatesByKey().values())
                 .containsExactly(KEYED_APP_STATE, KEYED_APP_STATE_DIFFERENT_KEY);
@@ -98,9 +125,10 @@
 
     @Test
     public void setStates_alreadyPopulated_addsToOnDeviceKeyedAppStates() {
-        mReporter.setStates(singletonList(KEYED_APP_STATE));
+        mReporter.setStates(singletonList(KEYED_APP_STATE), /* callback= */ null);
 
-        mReporter.setStates(singletonList(KEYED_APP_STATE_DIFFERENT_KEY));
+        mReporter.setStates(
+                singletonList(KEYED_APP_STATE_DIFFERENT_KEY), /* callback= */ null);
 
         assertThat(mReporter.getOnDeviceKeyedAppStates())
                 .containsExactly(KEYED_APP_STATE, KEYED_APP_STATE_DIFFERENT_KEY);
@@ -108,9 +136,10 @@
 
     @Test
     public void setStates_alreadyPopulated_addsToOnDeviceKeyedAppStatesByKey() {
-        mReporter.setStates(singletonList(KEYED_APP_STATE));
+        mReporter.setStates(singletonList(KEYED_APP_STATE), /* callback= */ null);
 
-        mReporter.setStates(singletonList(KEYED_APP_STATE_DIFFERENT_KEY));
+        mReporter.setStates(
+                singletonList(KEYED_APP_STATE_DIFFERENT_KEY), /* callback= */ null);
 
         assertThat(mReporter.getOnDeviceKeyedAppStatesByKey().values())
                 .containsExactly(KEYED_APP_STATE, KEYED_APP_STATE_DIFFERENT_KEY);
@@ -124,9 +153,10 @@
 
     @Test
     public void setStates_sameKeyAsPrevious_addsToOnDeviceKeyedAppStates() {
-        mReporter.setStates(singletonList(KEYED_APP_STATE));
+        mReporter.setStates(singletonList(KEYED_APP_STATE), /* callback= */ null);
 
-        mReporter.setStates(singletonList(KEYED_APP_STATE_DIFFERENT_MESSAGE));
+        mReporter.setStates(
+                singletonList(KEYED_APP_STATE_DIFFERENT_MESSAGE), /* callback= */ null);
 
         assertThat(mReporter.getOnDeviceKeyedAppStates())
                 .containsExactly(KEYED_APP_STATE, KEYED_APP_STATE_DIFFERENT_MESSAGE);
@@ -134,9 +164,10 @@
 
     @Test
     public void setStates_sameKeyAsPrevious_replacesOnDeviceKeyedAppStatesByKey() {
-        mReporter.setStates(singletonList(KEYED_APP_STATE));
+        mReporter.setStates(singletonList(KEYED_APP_STATE), /* callback= */ null);
 
-        mReporter.setStates(singletonList(KEYED_APP_STATE_DIFFERENT_MESSAGE));
+        mReporter.setStates(
+                singletonList(KEYED_APP_STATE_DIFFERENT_MESSAGE), /* callback= */ null);
 
         assertThat(mReporter.getOnDeviceKeyedAppStatesByKey().get(KEYED_APP_STATE.getKey()))
                 .isEqualTo(KEYED_APP_STATE_DIFFERENT_MESSAGE);
@@ -144,38 +175,47 @@
 
     @Test
     public void setStatesImmediate_clearsOnDeviceKeyedAppStates() {
-        mReporter.setStates(singletonList(KEYED_APP_STATE));
+        mReporter.setStates(singletonList(KEYED_APP_STATE), /* callback= */ null);
 
-        mReporter.setStatesImmediate(singletonList(KEYED_APP_STATE_DIFFERENT_KEY));
+        mReporter.setStatesImmediate(
+                singletonList(KEYED_APP_STATE_DIFFERENT_KEY), /* callback= */ null);
 
         assertThat(mReporter.getOnDeviceKeyedAppStates()).isEmpty();
     }
 
     @Test
     public void setStatesImmediate_clearsOnDeviceKeyedAppStatesByKey() {
-        mReporter.setStates(singletonList(KEYED_APP_STATE));
+        mReporter.setStates(singletonList(KEYED_APP_STATE), /* callback= */ null);
 
-        mReporter.setStatesImmediate(singletonList(KEYED_APP_STATE_DIFFERENT_KEY));
+        mReporter.setStatesImmediate(
+                singletonList(KEYED_APP_STATE_DIFFERENT_KEY), /* callback= */ null);
 
         assertThat(mReporter.getOnDeviceKeyedAppStatesByKey().keySet()).isEmpty();
     }
 
     @Test
     public void setStates_isNotRecordedInUploadedKeyedAppStates() {
-        mReporter.setStates(singletonList(KEYED_APP_STATE));
+        mReporter.setStates(singletonList(KEYED_APP_STATE), /* callback= */ null);
 
         assertThat(mReporter.getUploadedKeyedAppStates()).isEmpty();
     }
 
     @Test
     public void setStates_isNotRecordedInUploadedKeyedAppStatesByKey() {
-        mReporter.setStates(singletonList(KEYED_APP_STATE));
+        mReporter.setStates(singletonList(KEYED_APP_STATE), /* callback= */ null);
 
         assertThat(mReporter.getUploadedKeyedAppStatesByKey()).isEmpty();
     }
 
     @Test
     public void setStatesImmediate_single_isRecordedInUploadedKeyedAppStates() {
+        mReporter.setStatesImmediate(singletonList(KEYED_APP_STATE), /* callback= */ null);
+
+        assertThat(mReporter.getUploadedKeyedAppStates()).containsExactly(KEYED_APP_STATE);
+    }
+
+    @Test
+    public void setStatesImmediate_deprecated_isRecordedInUploadedKeyedAppStates() {
         mReporter.setStatesImmediate(singletonList(KEYED_APP_STATE));
 
         assertThat(mReporter.getUploadedKeyedAppStates()).containsExactly(KEYED_APP_STATE);
@@ -183,7 +223,7 @@
 
     @Test
     public void setStatesImmediate_single_isRecordedInUploadedKeyedAppStatesByKey() {
-        mReporter.setStatesImmediate(singletonList(KEYED_APP_STATE));
+        mReporter.setStatesImmediate(singletonList(KEYED_APP_STATE), /* callback= */ null);
 
         assertThat(mReporter.getUploadedKeyedAppStatesByKey().values())
                 .containsExactly(KEYED_APP_STATE);
@@ -193,7 +233,8 @@
 
     @Test
     public void setStatesImmediate_multiple_isRecordedInUploadedKeyedAppStates() {
-        mReporter.setStatesImmediate(asList(KEYED_APP_STATE, KEYED_APP_STATE_DIFFERENT_KEY));
+        mReporter.setStatesImmediate(
+                asList(KEYED_APP_STATE, KEYED_APP_STATE_DIFFERENT_KEY), /* callback= */ null);
 
         assertThat(mReporter.getUploadedKeyedAppStates())
                 .containsExactly(KEYED_APP_STATE, KEYED_APP_STATE_DIFFERENT_KEY);
@@ -201,7 +242,8 @@
 
     @Test
     public void setStatesImmediate_multiple_isRecordedInUploadedKeyedAppStatesByKey() {
-        mReporter.setStatesImmediate(asList(KEYED_APP_STATE, KEYED_APP_STATE_DIFFERENT_KEY));
+        mReporter.setStatesImmediate(
+                asList(KEYED_APP_STATE, KEYED_APP_STATE_DIFFERENT_KEY), /* callback= */ null);
 
         assertThat(mReporter.getUploadedKeyedAppStatesByKey().values())
                 .containsExactly(KEYED_APP_STATE, KEYED_APP_STATE_DIFFERENT_KEY);
@@ -215,9 +257,10 @@
 
     @Test
     public void setStatesImmediate_alreadyPopulated_addsToUploadedKeyedAppStates() {
-        mReporter.setStatesImmediate(singletonList(KEYED_APP_STATE));
+        mReporter.setStatesImmediate(singletonList(KEYED_APP_STATE), /* callback= */ null);
 
-        mReporter.setStatesImmediate(singletonList(KEYED_APP_STATE_DIFFERENT_KEY));
+        mReporter.setStatesImmediate(
+                singletonList(KEYED_APP_STATE_DIFFERENT_KEY), /* callback= */ null);
 
         assertThat(mReporter.getUploadedKeyedAppStates())
                 .containsExactly(KEYED_APP_STATE, KEYED_APP_STATE_DIFFERENT_KEY);
@@ -225,9 +268,10 @@
 
     @Test
     public void setStatesImmediate_alreadyPopulated_addsToUploadedKeyedAppStatesByKey() {
-        mReporter.setStatesImmediate(singletonList(KEYED_APP_STATE));
+        mReporter.setStatesImmediate(singletonList(KEYED_APP_STATE), /* callback= */ null);
 
-        mReporter.setStatesImmediate(singletonList(KEYED_APP_STATE_DIFFERENT_KEY));
+        mReporter.setStatesImmediate(
+                singletonList(KEYED_APP_STATE_DIFFERENT_KEY), /* callback= */ null);
 
         assertThat(mReporter.getUploadedKeyedAppStatesByKey().values())
                 .containsExactly(KEYED_APP_STATE, KEYED_APP_STATE_DIFFERENT_KEY);
@@ -241,9 +285,10 @@
 
     @Test
     public void setStatesImmediate_sameKeyAsPrevious_addsToUploadedKeyedAppStates() {
-        mReporter.setStatesImmediate(singletonList(KEYED_APP_STATE));
+        mReporter.setStatesImmediate(singletonList(KEYED_APP_STATE), /* callback= */ null);
 
-        mReporter.setStatesImmediate(singletonList(KEYED_APP_STATE_DIFFERENT_MESSAGE));
+        mReporter.setStatesImmediate(
+                singletonList(KEYED_APP_STATE_DIFFERENT_MESSAGE), /* callback= */ null);
 
         assertThat(mReporter.getUploadedKeyedAppStates())
                 .containsExactly(KEYED_APP_STATE, KEYED_APP_STATE_DIFFERENT_MESSAGE);
@@ -251,9 +296,10 @@
 
     @Test
     public void setStatesImmediate_sameKeyAsPrevious_replacesUploadedKeyedAppStatesByKey() {
-        mReporter.setStatesImmediate(singletonList(KEYED_APP_STATE));
+        mReporter.setStatesImmediate(singletonList(KEYED_APP_STATE), /* callback= */ null);
 
-        mReporter.setStatesImmediate(singletonList(KEYED_APP_STATE_DIFFERENT_MESSAGE));
+        mReporter.setStatesImmediate(
+                singletonList(KEYED_APP_STATE_DIFFERENT_MESSAGE), /* callback= */ null);
 
         assertThat(
                 mReporter.getUploadedKeyedAppStatesByKey().get(KEYED_APP_STATE.getKey()))
@@ -262,9 +308,10 @@
 
     @Test
     public void setStatesImmediate_uploadsPreviouslySetStates() {
-        mReporter.setStates(singletonList(KEYED_APP_STATE));
+        mReporter.setStates(singletonList(KEYED_APP_STATE), /* callback= */ null);
 
-        mReporter.setStatesImmediate(singletonList(KEYED_APP_STATE_DIFFERENT_KEY));
+        mReporter.setStatesImmediate(
+                singletonList(KEYED_APP_STATE_DIFFERENT_KEY), /* callback= */ null);
 
         assertThat(mReporter.getUploadedKeyedAppStatesByKey())
                 .containsKey(KEYED_APP_STATE.getKey());
@@ -272,21 +319,21 @@
 
     @Test
     public void setStatesImmediate_incrementsNumberOfUploads() {
-        mReporter.setStatesImmediate(singletonList(KEYED_APP_STATE));
+        mReporter.setStatesImmediate(singletonList(KEYED_APP_STATE), /* callback= */ null);
 
         assertThat(mReporter.getNumberOfUploads()).isEqualTo(1);
     }
 
     @Test
     public void setStates_single_isRecordedInKeyedAppStates() {
-        mReporter.setStates(singletonList(KEYED_APP_STATE));
+        mReporter.setStates(singletonList(KEYED_APP_STATE), /* callback= */ null);
 
         assertThat(mReporter.getKeyedAppStates()).containsExactly(KEYED_APP_STATE);
     }
 
     @Test
     public void setStates_single_isRecordedInKeyedAppStatesByKey() {
-        mReporter.setStates(singletonList(KEYED_APP_STATE));
+        mReporter.setStates(singletonList(KEYED_APP_STATE), /* callback= */ null);
 
         assertThat(mReporter.getKeyedAppStatesByKey().values())
                 .containsExactly(KEYED_APP_STATE);
@@ -296,7 +343,8 @@
 
     @Test
     public void setStates_multiple_isRecordedInKeyedAppStates() {
-        mReporter.setStates(asList(KEYED_APP_STATE, KEYED_APP_STATE_DIFFERENT_KEY));
+        mReporter.setStates(
+                asList(KEYED_APP_STATE, KEYED_APP_STATE_DIFFERENT_KEY), /* callback= */ null);
 
         assertThat(mReporter.getKeyedAppStates())
                 .containsExactly(KEYED_APP_STATE, KEYED_APP_STATE_DIFFERENT_KEY);
@@ -304,7 +352,8 @@
 
     @Test
     public void setStates_multiple_isRecordedInKeyedAppStatesByKey() {
-        mReporter.setStates(asList(KEYED_APP_STATE, KEYED_APP_STATE_DIFFERENT_KEY));
+        mReporter.setStates(
+                asList(KEYED_APP_STATE, KEYED_APP_STATE_DIFFERENT_KEY), /* callback= */ null);
 
         assertThat(mReporter.getKeyedAppStatesByKey().values())
                 .containsExactly(KEYED_APP_STATE, KEYED_APP_STATE_DIFFERENT_KEY);
@@ -316,9 +365,10 @@
 
     @Test
     public void setStates_alreadyPopulated_addsToKeyedAppStates() {
-        mReporter.setStates(singletonList(KEYED_APP_STATE));
+        mReporter.setStates(singletonList(KEYED_APP_STATE), /* callback= */ null);
 
-        mReporter.setStates(singletonList(KEYED_APP_STATE_DIFFERENT_KEY));
+        mReporter.setStates(
+                singletonList(KEYED_APP_STATE_DIFFERENT_KEY), /* callback= */ null);
 
         assertThat(mReporter.getKeyedAppStates())
                 .containsExactly(KEYED_APP_STATE, KEYED_APP_STATE_DIFFERENT_KEY);
@@ -326,9 +376,10 @@
 
     @Test
     public void setStates_alreadyPopulated_addsToKeyedAppStatesByKey() {
-        mReporter.setStates(singletonList(KEYED_APP_STATE));
+        mReporter.setStates(singletonList(KEYED_APP_STATE), /* callback= */ null);
 
-        mReporter.setStates(singletonList(KEYED_APP_STATE_DIFFERENT_KEY));
+        mReporter.setStates(
+                singletonList(KEYED_APP_STATE_DIFFERENT_KEY), /* callback= */ null);
 
         assertThat(mReporter.getKeyedAppStatesByKey().values())
                 .containsExactly(KEYED_APP_STATE, KEYED_APP_STATE_DIFFERENT_KEY);
@@ -340,9 +391,10 @@
 
     @Test
     public void setStates_sameKeyAsPrevious_addsToKeyedAppStates() {
-        mReporter.setStates(singletonList(KEYED_APP_STATE));
+        mReporter.setStates(singletonList(KEYED_APP_STATE), /* callback= */ null);
 
-        mReporter.setStates(singletonList(KEYED_APP_STATE_DIFFERENT_MESSAGE));
+        mReporter.setStates(
+                singletonList(KEYED_APP_STATE_DIFFERENT_MESSAGE), /* callback= */ null);
 
         assertThat(mReporter.getKeyedAppStates())
                 .containsExactly(KEYED_APP_STATE, KEYED_APP_STATE_DIFFERENT_MESSAGE);
@@ -350,9 +402,10 @@
 
     @Test
     public void setStates_sameKeyAsPrevious_replacesKeyedAppStatesByKey() {
-        mReporter.setStates(singletonList(KEYED_APP_STATE));
+        mReporter.setStates(singletonList(KEYED_APP_STATE), /* callback= */ null);
 
-        mReporter.setStates(singletonList(KEYED_APP_STATE_DIFFERENT_MESSAGE));
+        mReporter.setStates(
+                singletonList(KEYED_APP_STATE_DIFFERENT_MESSAGE), /* callback= */ null);
 
         assertThat(
                 mReporter.getKeyedAppStatesByKey().get(KEYED_APP_STATE.getKey()))
@@ -361,9 +414,10 @@
 
     @Test
     public void setStatesImmediate_doesNotClearKeyedAppStates() {
-        mReporter.setStates(singletonList(KEYED_APP_STATE));
+        mReporter.setStates(singletonList(KEYED_APP_STATE), /* callback= */ null);
 
-        mReporter.setStatesImmediate(singletonList(KEYED_APP_STATE_DIFFERENT_KEY));
+        mReporter.setStatesImmediate(
+                singletonList(KEYED_APP_STATE_DIFFERENT_KEY), /* callback= */ null);
 
         assertThat(mReporter.getKeyedAppStates())
                 .containsExactly(KEYED_APP_STATE, KEYED_APP_STATE_DIFFERENT_KEY);
@@ -371,9 +425,10 @@
 
     @Test
     public void setStatesImmediate_doesNotClearKeyedAppStatesByKey() {
-        mReporter.setStates(singletonList(KEYED_APP_STATE));
+        mReporter.setStates(singletonList(KEYED_APP_STATE), /* callback= */ null);
 
-        mReporter.setStatesImmediate(singletonList(KEYED_APP_STATE_DIFFERENT_KEY));
+        mReporter.setStatesImmediate(
+                singletonList(KEYED_APP_STATE_DIFFERENT_KEY), /* callback= */ null);
 
         assertThat(mReporter.getKeyedAppStatesByKey().keySet())
                 .containsExactly(KEYED_APP_STATE.getKey(), KEYED_APP_STATE_DIFFERENT_KEY.getKey());
@@ -381,21 +436,23 @@
 
     @Test
     public void getOnDeviceKeyedAppStates_returnsCopy() {
-        mReporter.setStates(singletonList(KEYED_APP_STATE));
+        mReporter.setStates(singletonList(KEYED_APP_STATE), /* callback= */ null);
         List<KeyedAppState> beforeOnDeviceKeyedAppStates = mReporter.getOnDeviceKeyedAppStates();
 
-        mReporter.setStates(singletonList(KEYED_APP_STATE_DIFFERENT_KEY));
+        mReporter.setStates(
+                singletonList(KEYED_APP_STATE_DIFFERENT_KEY), /* callback= */ null);
 
         assertThat(beforeOnDeviceKeyedAppStates).doesNotContain(KEYED_APP_STATE_DIFFERENT_KEY);
     }
 
     @Test
     public void getOnDeviceKeyedAppStatesByKey_returnsCopy() {
-        mReporter.setStates(singletonList(KEYED_APP_STATE));
+        mReporter.setStates(singletonList(KEYED_APP_STATE), /* callback= */ null);
         Map<String, KeyedAppState> beforeOnDeviceKeyedAppStates =
                 mReporter.getOnDeviceKeyedAppStatesByKey();
 
-        mReporter.setStates(singletonList(KEYED_APP_STATE_DIFFERENT_KEY));
+        mReporter.setStates(
+                singletonList(KEYED_APP_STATE_DIFFERENT_KEY), /* callback= */ null);
 
         assertThat(beforeOnDeviceKeyedAppStates)
                 .doesNotContainKey(KEYED_APP_STATE_DIFFERENT_KEY.getKey());
@@ -403,21 +460,23 @@
 
     @Test
     public void getKeyedAppStates_returnsCopy() {
-        mReporter.setStates(singletonList(KEYED_APP_STATE));
+        mReporter.setStates(singletonList(KEYED_APP_STATE), /* callback= */ null);
         List<KeyedAppState> beforeKeyedAppStates = mReporter.getKeyedAppStates();
 
-        mReporter.setStates(singletonList(KEYED_APP_STATE_DIFFERENT_KEY));
+        mReporter.setStates(
+                singletonList(KEYED_APP_STATE_DIFFERENT_KEY), /* callback= */ null);
 
         assertThat(beforeKeyedAppStates).doesNotContain(KEYED_APP_STATE_DIFFERENT_KEY);
     }
 
     @Test
     public void getKeyedAppStatesByKey_returnsCopy() {
-        mReporter.setStates(singletonList(KEYED_APP_STATE));
+        mReporter.setStates(singletonList(KEYED_APP_STATE), /* callback= */ null);
         Map<String, KeyedAppState> beforeKeyedAppStates =
                 mReporter.getKeyedAppStatesByKey();
 
-        mReporter.setStates(singletonList(KEYED_APP_STATE_DIFFERENT_KEY));
+        mReporter.setStates(
+                singletonList(KEYED_APP_STATE_DIFFERENT_KEY), /* callback= */ null);
 
         assertThat(beforeKeyedAppStates)
                 .doesNotContainKey(KEYED_APP_STATE_DIFFERENT_KEY.getKey());
@@ -425,21 +484,23 @@
 
     @Test
     public void getUploadedKeyedAppStates_returnsCopy() {
-        mReporter.setStatesImmediate(singletonList(KEYED_APP_STATE));
+        mReporter.setStatesImmediate(singletonList(KEYED_APP_STATE), /* callback= */ null);
         List<KeyedAppState> beforeUploadedKeyedAppStates = mReporter.getUploadedKeyedAppStates();
 
-        mReporter.setStatesImmediate(singletonList(KEYED_APP_STATE_DIFFERENT_KEY));
+        mReporter.setStatesImmediate(
+                singletonList(KEYED_APP_STATE_DIFFERENT_KEY), /* callback= */ null);
 
         assertThat(beforeUploadedKeyedAppStates).doesNotContain(KEYED_APP_STATE_DIFFERENT_KEY);
     }
 
     @Test
     public void getUploadedKeyedAppStatesByKey_returnsCopy() {
-        mReporter.setStatesImmediate(singletonList(KEYED_APP_STATE));
+        mReporter.setStatesImmediate(singletonList(KEYED_APP_STATE), /* callback= */ null);
         Map<String, KeyedAppState> beforeUploadedKeyedAppStates =
                 mReporter.getUploadedKeyedAppStatesByKey();
 
-        mReporter.setStatesImmediate(singletonList(KEYED_APP_STATE_DIFFERENT_KEY));
+        mReporter.setStatesImmediate(
+                singletonList(KEYED_APP_STATE_DIFFERENT_KEY), /* callback= */ null);
 
         assertThat(beforeUploadedKeyedAppStates)
                 .doesNotContainKey(KEYED_APP_STATE_DIFFERENT_KEY.getKey());
diff --git a/enterprise/feedback/testing/src/test/java/androidx/enterprise/feedback/TestKeyedAppStatesCallback.java b/enterprise/feedback/testing/src/test/java/androidx/enterprise/feedback/TestKeyedAppStatesCallback.java
new file mode 100644
index 0000000..37e05c9
--- /dev/null
+++ b/enterprise/feedback/testing/src/test/java/androidx/enterprise/feedback/TestKeyedAppStatesCallback.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2020 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.enterprise.feedback;
+
+import androidx.annotation.Nullable;
+
+class TestKeyedAppStatesCallback implements KeyedAppStatesCallback {
+
+    int mTotalResults = 0;
+    int mLatestState = -1;
+    Throwable mLatestThrowable = null;
+
+    @Override
+    public void onResult(int state, @Nullable Throwable throwable) {
+        mTotalResults++;
+        mLatestState = state;
+        mLatestThrowable = throwable;
+    }
+}
diff --git a/fragment/fragment/src/androidTest/java/androidx/fragment/app/BackStackStateTest.kt b/fragment/fragment/src/androidTest/java/androidx/fragment/app/BackStackStateTest.kt
index 2843ded..31abb96 100644
--- a/fragment/fragment/src/androidTest/java/androidx/fragment/app/BackStackStateTest.kt
+++ b/fragment/fragment/src/androidTest/java/androidx/fragment/app/BackStackStateTest.kt
@@ -269,9 +269,35 @@
         val fm = fc.supportFragmentManager
 
         val fragment = StrictViewFragment()
+
+        fm.beginTransaction()
+            .add(android.R.id.content, fragment)
+            .setReorderingAllowed(true)
+            .setMaxLifecycle(fragment, Lifecycle.State.INITIALIZED)
+            .commitNow()
+
+        assertThat(fragment.lifecycle.currentState).isEqualTo(Lifecycle.State.INITIALIZED)
+
+        assertThat(fragment.calledOnResume).isFalse()
+    }
+
+    @Test
+    @UiThreadTest
+    fun setMaxLifecycleInitializedAfterCreated() {
+        val viewModelStore = ViewModelStore()
+        val fc = activityRule.startupFragmentController(viewModelStore)
+
+        val fm = fc.supportFragmentManager
+
+        val fragment = StrictViewFragment()
+
+        fm.beginTransaction()
+            .add(android.R.id.content, fragment)
+            .setMaxLifecycle(fragment, Lifecycle.State.CREATED)
+            .commitNow()
+
         try {
             fm.beginTransaction()
-                .add(android.R.id.content, fragment)
                 .setMaxLifecycle(fragment, Lifecycle.State.INITIALIZED)
                 .commitNow()
             fail(
@@ -281,7 +307,10 @@
         } catch (e: IllegalArgumentException) {
             assertThat(e)
                 .hasMessageThat()
-                .contains("Cannot set maximum Lifecycle below CREATED")
+                .contains(
+                    "Cannot set maximum Lifecycle to INITIALIZED after the Fragment has been " +
+                            "created"
+                )
         }
     }
 }
diff --git a/fragment/fragment/src/androidTest/java/androidx/fragment/app/ViewModelTest.kt b/fragment/fragment/src/androidTest/java/androidx/fragment/app/ViewModelTest.kt
index 3edf294..346918f 100644
--- a/fragment/fragment/src/androidTest/java/androidx/fragment/app/ViewModelTest.kt
+++ b/fragment/fragment/src/androidTest/java/androidx/fragment/app/ViewModelTest.kt
@@ -16,6 +16,7 @@
 
 package androidx.fragment.app
 
+import androidx.fragment.app.test.EmptyFragmentTestActivity
 import androidx.fragment.app.test.TestViewModel
 import androidx.fragment.app.test.ViewModelActivity
 import androidx.fragment.app.test.ViewModelActivity.ViewModelFragment
@@ -42,6 +43,60 @@
     }
 
     @Test
+    fun testMaxLifecycleInitializedFragment() {
+        with(ActivityScenario.launch(EmptyFragmentTestActivity::class.java)) {
+            withActivity {
+                val fragment = StrictFragment()
+                supportFragmentManager.beginTransaction()
+                    .setReorderingAllowed(true)
+                    .add(android.R.id.content, fragment)
+                    .setMaxLifecycle(fragment, Lifecycle.State.INITIALIZED)
+                    .commitNow()
+
+                try {
+                    fragment.viewModelStore
+                } catch (e: IllegalStateException) {
+                    assertThat(e).hasMessageThat().contains(
+                        "Calling getViewModelStore() before a Fragment " +
+                                "reaches onCreate() when using setMaxLifecycle(INITIALIZED) is " +
+                                "not supported"
+                    )
+                }
+            }
+        }
+    }
+
+    @Test
+    fun testMaxLifecycleInitializedNestedFragment() {
+        with(ActivityScenario.launch(EmptyFragmentTestActivity::class.java)) {
+            withActivity {
+                val fragment = StrictFragment()
+                val childFragment = StrictFragment()
+
+                supportFragmentManager.beginTransaction()
+                    .setReorderingAllowed(true)
+                    .add(android.R.id.content, fragment)
+                    .setMaxLifecycle(fragment, Lifecycle.State.INITIALIZED)
+                    .commitNow()
+
+                fragment.childFragmentManager.beginTransaction()
+                    .add(android.R.id.content, childFragment)
+                    .commitNow()
+
+                try {
+                    childFragment.viewModelStore
+                } catch (e: IllegalStateException) {
+                    assertThat(e).hasMessageThat().contains(
+                        "Calling getViewModelStore() before a Fragment " +
+                                "reaches onCreate() when using setMaxLifecycle(INITIALIZED) is " +
+                                "not supported"
+                    )
+                }
+            }
+        }
+    }
+
+    @Test
     fun testSameActivityViewModels() {
         with(ActivityScenario.launch(ViewModelActivity::class.java)) {
             val activityModel = withActivity { activityModel }
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/BackStackRecord.java b/fragment/fragment/src/main/java/androidx/fragment/app/BackStackRecord.java
index b095212..4bc88fb 100644
--- a/fragment/fragment/src/main/java/androidx/fragment/app/BackStackRecord.java
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/BackStackRecord.java
@@ -248,9 +248,9 @@
             throw new IllegalArgumentException("Cannot setMaxLifecycle for Fragment not attached to"
                     + " FragmentManager " + mManager);
         }
-        if (!state.isAtLeast(Lifecycle.State.CREATED)) {
-            throw new IllegalArgumentException("Cannot set maximum Lifecycle below "
-                    + Lifecycle.State.CREATED);
+        if (state == Lifecycle.State.INITIALIZED && fragment.mState > Fragment.INITIALIZING) {
+            throw new IllegalArgumentException("Cannot set maximum Lifecycle to " + state
+                    + " after the Fragment has been created");
         }
         return super.setMaxLifecycle(fragment, state);
     }
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/Fragment.java b/fragment/fragment/src/main/java/androidx/fragment/app/Fragment.java
index e7ebc3d..c7530f64 100644
--- a/fragment/fragment/src/main/java/androidx/fragment/app/Fragment.java
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/Fragment.java
@@ -397,9 +397,22 @@
         if (mFragmentManager == null) {
             throw new IllegalStateException("Can't access ViewModels from detached fragment");
         }
+        if (getMinimumMaxLifecycleState() == Lifecycle.State.INITIALIZED.ordinal()) {
+            throw new IllegalStateException("Calling getViewModelStore() before a Fragment "
+                    + "reaches onCreate() when using setMaxLifecycle(INITIALIZED) is not "
+                    + "supported");
+        }
         return mFragmentManager.getViewModelStore(this);
     }
 
+
+    private int getMinimumMaxLifecycleState() {
+        if (mMaxState == Lifecycle.State.INITIALIZED || mParentFragment == null) {
+            return mMaxState.ordinal();
+        }
+        return Math.min(mMaxState.ordinal(), mParentFragment.getMinimumMaxLifecycleState());
+    }
+
     /**
      * {@inheritDoc}
      *
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/FragmentStateManager.java b/fragment/fragment/src/main/java/androidx/fragment/app/FragmentStateManager.java
index 13a25cf..00c1d0b 100644
--- a/fragment/fragment/src/main/java/androidx/fragment/app/FragmentStateManager.java
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/FragmentStateManager.java
@@ -238,6 +238,9 @@
             case CREATED:
                 maxState = Math.min(maxState, Fragment.CREATED);
                 break;
+            case INITIALIZED:
+                maxState = Math.min(maxState, Fragment.ATTACHED);
+                break;
             default:
                 maxState = Math.min(maxState, Fragment.INITIALIZING);
         }
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/FragmentTransaction.java b/fragment/fragment/src/main/java/androidx/fragment/app/FragmentTransaction.java
index 7c6c6c72..ec4771f 100644
--- a/fragment/fragment/src/main/java/androidx/fragment/app/FragmentTransaction.java
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/FragmentTransaction.java
@@ -454,9 +454,10 @@
      * already above the received state, it will be forced down to the correct state.
      *
      * <p>The fragment provided must currently be added to the FragmentManager to have it's
-     * Lifecycle state capped, or previously added as part of this transaction. The
-     * {@link Lifecycle.State} passed in must at least be {@link Lifecycle.State#CREATED}, otherwise
-     * an {@link IllegalArgumentException} will be thrown.</p>
+     * Lifecycle state capped, or previously added as part of this transaction. If the
+     * {@link Lifecycle.State#INITIALIZED} is passed in as the {@link Lifecycle.State} and the
+     * provided fragment has already moved beyond {@link Lifecycle.State#INITIALIZED}, an
+     * {@link IllegalArgumentException} will be thrown.</p>
      *
      * @param fragment the fragment to have it's state capped.
      * @param state the ceiling state for the fragment.
diff --git a/leanback/leanback/src/androidTest/java/androidx/leanback/widget/GridWidgetTest.java b/leanback/leanback/src/androidTest/java/androidx/leanback/widget/GridWidgetTest.java
index 9627125..382e579 100644
--- a/leanback/leanback/src/androidTest/java/androidx/leanback/widget/GridWidgetTest.java
+++ b/leanback/leanback/src/androidTest/java/androidx/leanback/widget/GridWidgetTest.java
@@ -4902,6 +4902,70 @@
     }
 
     @Test
+    public void testAccessibilityFocusOutFrontEnd_actionsAvailable() throws Throwable {
+        Intent intent = new Intent();
+        intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
+                R.layout.horizontal_linear);
+        int[] items = new int[5];
+        for (int i = 0; i < items.length; i++) {
+            items[i] = 300;
+        }
+        intent.putExtra(GridActivity.EXTRA_ITEMS, items);
+        intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
+        initActivity(intent);
+        mOrientation = BaseGridView.HORIZONTAL;
+        mNumRows = 1;
+        final RecyclerViewAccessibilityDelegate delegateCompat = mGridView
+                .getCompatAccessibilityDelegate();
+        final AccessibilityNodeInfoCompat info1 = AccessibilityNodeInfoCompat.obtain();
+        // Test not allowing going out both ends
+        mLayoutManager.setFocusOutAllowed(/* throughFront= */ false,
+                /* throughEnd= */ false);
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                delegateCompat.onInitializeAccessibilityNodeInfo(mGridView, info1);
+            }
+        });
+        // When not allowing jumping out both end, handle action scroll backward/forward to block
+        // it.
+        if (Build.VERSION.SDK_INT >= 21) {
+            assertTrue(hasAction(info1,
+                    AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_RIGHT));
+            assertTrue(hasAction(info1,
+                    AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_LEFT));
+        } else {
+            assertTrue(hasAction(info1,
+                    AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD));
+            assertTrue(hasAction(info1,
+                    AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD));
+        }
+        final AccessibilityNodeInfoCompat info2 = AccessibilityNodeInfoCompat.obtain();
+        // Test allowing focus to jump out at front when reaching front.
+        mLayoutManager.setFocusOutAllowed(/* throughFront= */ true,
+                /* throughEnd= */ false);
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                delegateCompat.onInitializeAccessibilityNodeInfo(mGridView, info2);
+            }
+        });
+        // When only allowing jumping out front, block action scroll backward when reaching front
+        // for Talkback to jump focus out.
+        if (Build.VERSION.SDK_INT >= 21) {
+            assertFalse(hasAction(info2,
+                    AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_LEFT));
+            assertTrue(hasAction(info2,
+                    AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_RIGHT));
+        } else {
+            assertFalse(hasAction(info2,
+                    AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD));
+            assertTrue(hasAction(info2,
+                    AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD));
+        }
+    }
+
+    @Test
     public void testAccessibilitySaveContextCrash() throws Throwable {
         Intent intent = new Intent();
         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
diff --git a/leanback/leanback/src/main/java/androidx/leanback/widget/GridLayoutManager.java b/leanback/leanback/src/main/java/androidx/leanback/widget/GridLayoutManager.java
index e15e2c9..3a86542 100644
--- a/leanback/leanback/src/main/java/androidx/leanback/widget/GridLayoutManager.java
+++ b/leanback/leanback/src/main/java/androidx/leanback/widget/GridLayoutManager.java
@@ -37,6 +37,7 @@
 import android.view.View.MeasureSpec;
 import android.view.ViewGroup;
 import android.view.ViewGroup.MarginLayoutParams;
+import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.AccelerateDecelerateInterpolator;
 
 import androidx.annotation.VisibleForTesting;
@@ -3758,20 +3759,40 @@
                 }
             }
         }
-        switch (translatedAction) {
-            case AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD:
-                processPendingMovement(false);
-                processSelectionMoves(false, -1);
-                break;
-            case AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD:
-                processPendingMovement(true);
-                processSelectionMoves(false, 1);
-                break;
+        boolean scrollingReachedBeginning = (mFocusPosition == 0
+                && translatedAction == AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD);
+        boolean scrollingReachedEnd = (mFocusPosition == state.getItemCount() - 1
+                && translatedAction == AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD);
+        if (scrollingReachedBeginning || scrollingReachedEnd) {
+            // Send a fake scroll completion event to notify Talkback that the scroll event was
+            // successful. Hence, Talkback will only look for next focus within the RecyclerView.
+            // Not sending this will result in Talkback classifying it as a failed scroll event, and
+            // will try to jump focus out of the RecyclerView.
+            // We know at this point that either focusOutFront or focusOutEnd is true (or both),
+            // because otherwise, we never hit ACTION_SCROLL_BACKWARD/FORWARD here.
+            sendTypeViewScrolledAccessibilityEvent();
+        } else {
+            switch (translatedAction) {
+                case AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD:
+                    processPendingMovement(false);
+                    processSelectionMoves(false, -1);
+                    break;
+                case AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD:
+                    processPendingMovement(true);
+                    processSelectionMoves(false, 1);
+                    break;
+            }
         }
         leaveContext();
         return true;
     }
 
+    private void sendTypeViewScrolledAccessibilityEvent() {
+        AccessibilityEvent event = AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_SCROLLED);
+        mBaseGridView.onInitializeAccessibilityEvent(event);
+        mBaseGridView.requestSendAccessibilityEvent(mBaseGridView, event);
+    }
+
     /*
      * Move mFocusPosition multiple steps on the same row in main direction.
      * Stops when moves are all consumed or reach first/last visible item.
@@ -3826,45 +3847,58 @@
         return moves;
     }
 
+    private void addA11yActionMovingBackward(AccessibilityNodeInfoCompat info,
+            boolean reverseFlowPrimary) {
+        if (Build.VERSION.SDK_INT >= 23) {
+            if (mOrientation == HORIZONTAL) {
+                info.addAction(reverseFlowPrimary
+                        ? AccessibilityNodeInfoCompat.AccessibilityActionCompat
+                        .ACTION_SCROLL_RIGHT :
+                        AccessibilityNodeInfoCompat.AccessibilityActionCompat
+                                .ACTION_SCROLL_LEFT);
+            } else {
+                info.addAction(
+                        AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_UP);
+            }
+        } else {
+            info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD);
+        }
+        info.setScrollable(true);
+    }
+
+    private void addA11yActionMovingForward(AccessibilityNodeInfoCompat info,
+            boolean reverseFlowPrimary) {
+        if (Build.VERSION.SDK_INT >= 23) {
+            if (mOrientation == HORIZONTAL) {
+                info.addAction(reverseFlowPrimary
+                        ? AccessibilityNodeInfoCompat.AccessibilityActionCompat
+                        .ACTION_SCROLL_LEFT :
+                        AccessibilityNodeInfoCompat.AccessibilityActionCompat
+                                .ACTION_SCROLL_RIGHT);
+            } else {
+                info.addAction(AccessibilityNodeInfoCompat.AccessibilityActionCompat
+                        .ACTION_SCROLL_DOWN);
+            }
+        } else {
+            info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD);
+        }
+        info.setScrollable(true);
+    }
+
     @Override
     public void onInitializeAccessibilityNodeInfo(Recycler recycler, State state,
             AccessibilityNodeInfoCompat info) {
         saveContext(recycler, state);
         int count = state.getItemCount();
+        // reverseFlowPrimary is whether we are in LTR/RTL mode.
         boolean reverseFlowPrimary = (mFlag & PF_REVERSE_FLOW_PRIMARY) != 0;
-        if (count > 1 && !isItemFullyVisible(0)) {
-            if (Build.VERSION.SDK_INT >= 23) {
-                if (mOrientation == HORIZONTAL) {
-                    info.addAction(reverseFlowPrimary
-                            ? AccessibilityNodeInfoCompat.AccessibilityActionCompat
-                                    .ACTION_SCROLL_RIGHT :
-                            AccessibilityNodeInfoCompat.AccessibilityActionCompat
-                                    .ACTION_SCROLL_LEFT);
-                } else {
-                    info.addAction(
-                            AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_UP);
-                }
-            } else {
-                info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD);
-            }
-            info.setScrollable(true);
+        // If focusOutFront/focusOutEnd is false, override Talkback in handling
+        // backward/forward actions by adding such actions to supported action list.
+        if ((mFlag & PF_FOCUS_OUT_FRONT) == 0 || (count > 1 && !isItemFullyVisible(0))) {
+            addA11yActionMovingBackward(info, reverseFlowPrimary);
         }
-        if (count > 1 && !isItemFullyVisible(count - 1)) {
-            if (Build.VERSION.SDK_INT >= 23) {
-                if (mOrientation == HORIZONTAL) {
-                    info.addAction(reverseFlowPrimary
-                            ? AccessibilityNodeInfoCompat.AccessibilityActionCompat
-                                    .ACTION_SCROLL_LEFT :
-                            AccessibilityNodeInfoCompat.AccessibilityActionCompat
-                                    .ACTION_SCROLL_RIGHT);
-                } else {
-                    info.addAction(AccessibilityNodeInfoCompat.AccessibilityActionCompat
-                                    .ACTION_SCROLL_DOWN);
-                }
-            } else {
-                info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD);
-            }
-            info.setScrollable(true);
+        if ((mFlag & PF_FOCUS_OUT_END) == 0 || (count > 1 && !isItemFullyVisible(count - 1))) {
+            addA11yActionMovingForward(info, reverseFlowPrimary);
         }
         final AccessibilityNodeInfoCompat.CollectionInfoCompat collectionInfo =
                 AccessibilityNodeInfoCompat.CollectionInfoCompat
diff --git a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRoute2ProviderServiceAdapter.java b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRoute2ProviderServiceAdapter.java
index c4e8d90..73fa138 100644
--- a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRoute2ProviderServiceAdapter.java
+++ b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRoute2ProviderServiceAdapter.java
@@ -202,7 +202,7 @@
             notifyRequestFailed(requestId, REASON_INVALID_COMMAND);
             return;
         }
-        sessionRecord.release();
+        sessionRecord.release(/*shouldUnselect=*/true);
     }
 
     @Override
@@ -502,7 +502,7 @@
             sessionRecord = mSessionRecords.remove(sessionId);
         }
         if (sessionRecord != null) {
-            sessionRecord.release();
+            sessionRecord.release(/*shouldUnselect=*/false);
         }
     }
 
@@ -747,7 +747,7 @@
             }
         }
 
-        public void release() {
+        public void release(boolean shouldUnselect) {
             if (!mIsReleased) {
                 // Release member controllers
                 if ((mFlags & (SESSION_FLAG_MR2 | SESSION_FLAG_GROUP))
@@ -755,7 +755,7 @@
                     updateMemberRouteControllers(null, mSessionInfo, null);
                 }
 
-                if ((mFlags & SESSION_FLAG_MR2) != 0) {
+                if (shouldUnselect) {
                     mController.onUnselect(MediaRouter.UNSELECT_REASON_STOPPED);
                     mController.onRelease();
                 }
diff --git a/paging/common/api/current.txt b/paging/common/api/current.txt
index efc3cb0..31c6f43 100644
--- a/paging/common/api/current.txt
+++ b/paging/common/api/current.txt
@@ -132,9 +132,6 @@
     enum_constant public static final androidx.paging.LoadType REFRESH;
   }
 
-  public final class PageEventKt {
-  }
-
   @Deprecated public abstract class PageKeyedDataSource<Key, Value> extends androidx.paging.DataSource<Key,Value> {
     ctor @Deprecated public PageKeyedDataSource();
     method @Deprecated public abstract void loadAfter(androidx.paging.PageKeyedDataSource.LoadParams<Key> params, androidx.paging.PageKeyedDataSource.LoadCallback<Key,Value> callback);
diff --git a/paging/common/api/public_plus_experimental_current.txt b/paging/common/api/public_plus_experimental_current.txt
index 179870c..b3261e2 100644
--- a/paging/common/api/public_plus_experimental_current.txt
+++ b/paging/common/api/public_plus_experimental_current.txt
@@ -134,9 +134,6 @@
     enum_constant public static final androidx.paging.LoadType REFRESH;
   }
 
-  public final class PageEventKt {
-  }
-
   @Deprecated public abstract class PageKeyedDataSource<Key, Value> extends androidx.paging.DataSource<Key,Value> {
     ctor @Deprecated public PageKeyedDataSource();
     method @Deprecated public abstract void loadAfter(androidx.paging.PageKeyedDataSource.LoadParams<Key> params, androidx.paging.PageKeyedDataSource.LoadCallback<Key,Value> callback);
diff --git a/paging/common/api/restricted_current.txt b/paging/common/api/restricted_current.txt
index efc3cb0..31c6f43 100644
--- a/paging/common/api/restricted_current.txt
+++ b/paging/common/api/restricted_current.txt
@@ -132,9 +132,6 @@
     enum_constant public static final androidx.paging.LoadType REFRESH;
   }
 
-  public final class PageEventKt {
-  }
-
   @Deprecated public abstract class PageKeyedDataSource<Key, Value> extends androidx.paging.DataSource<Key,Value> {
     ctor @Deprecated public PageKeyedDataSource();
     method @Deprecated public abstract void loadAfter(androidx.paging.PageKeyedDataSource.LoadParams<Key> params, androidx.paging.PageKeyedDataSource.LoadCallback<Key,Value> callback);
diff --git a/paging/common/src/main/kotlin/androidx/paging/CachedPageEventFlow.kt b/paging/common/src/main/kotlin/androidx/paging/CachedPageEventFlow.kt
index e509aeb..1b25396 100644
--- a/paging/common/src/main/kotlin/androidx/paging/CachedPageEventFlow.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/CachedPageEventFlow.kt
@@ -54,6 +54,7 @@
      * This flag ensures that we do not try to collect from upstream more than once.
      */
     private val collectedFromSource = AtomicBoolean(false)
+
     /**
      * Shared upstream.
      * Note that, if upstream flow ends, re-subscribing to this will not re-collect from upstream
@@ -165,6 +166,7 @@
     private val list = FlattenedPageEventStorage<T>()
     private var snapshots = listOf<TemporaryDownstream<T>>()
     private val lock = Mutex()
+
     /**
      * Record the event.
      * This sends the event into storage but also into any other active TemporaryDownstream.
@@ -233,15 +235,11 @@
         when (event.loadType) {
             LoadType.PREPEND -> {
                 placeholdersBefore = event.placeholdersRemaining
-                repeat(event.count) {
-                    pages.removeFirst()
-                }
+                repeat(event.pageCount) { pages.removeFirst() }
             }
             LoadType.APPEND -> {
                 placeholdersAfter = event.placeholdersRemaining
-                repeat(event.count) {
-                    pages.removeLast()
-                }
+                repeat(event.pageCount) { pages.removeLast() }
             }
             else -> throw IllegalArgumentException("Page drop type must be prepend or append")
         }
diff --git a/paging/common/src/main/kotlin/androidx/paging/PageEvent.kt b/paging/common/src/main/kotlin/androidx/paging/PageEvent.kt
index ec9b15a..47bbb74 100644
--- a/paging/common/src/main/kotlin/androidx/paging/PageEvent.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/PageEvent.kt
@@ -19,8 +19,6 @@
 import androidx.paging.LoadType.APPEND
 import androidx.paging.LoadType.PREPEND
 import androidx.paging.LoadType.REFRESH
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.map
 
 /**
  * Events in the stream from paging fetch logic to UI.
@@ -28,6 +26,8 @@
  * Every event sent to the UI is a PageEvent, and will be processed atomically.
  */
 internal sealed class PageEvent<T : Any> {
+    // Intentional to prefer Refresh, Prepend, Append constructors from Companion.
+    @Suppress("DataClassPrivateConstructor")
     data class Insert<T : Any> private constructor(
         val loadType: LoadType,
         val pages: List<TransformablePage<T>>,
@@ -62,10 +62,10 @@
 
         override suspend fun <R : Any> map(transform: suspend (T) -> R): PageEvent<R> = mapPages {
             TransformablePage(
-                originalPageOffset = it.originalPageOffset,
+                originalPageOffsets = it.originalPageOffsets,
                 data = it.data.map { item -> transform(item) },
-                originalPageSize = it.originalPageSize,
-                originalIndices = it.originalIndices
+                hintOriginalPageOffset = it.hintOriginalPageOffset,
+                hintOriginalIndices = it.hintOriginalIndices
             )
         }
 
@@ -76,16 +76,16 @@
             val originalIndices = mutableListOf<Int>()
             it.data.forEachIndexed { index, t ->
                 data += transform(t)
-                val indexToStore = it.originalIndices?.get(index) ?: index
+                val indexToStore = it.hintOriginalIndices?.get(index) ?: index
                 while (originalIndices.size < data.size) {
                     originalIndices.add(indexToStore)
                 }
             }
             TransformablePage(
-                originalPageOffset = it.originalPageOffset,
+                originalPageOffsets = it.originalPageOffsets,
                 data = data,
-                originalPageSize = it.originalPageSize,
-                originalIndices = originalIndices
+                hintOriginalPageOffset = it.hintOriginalPageOffset,
+                hintOriginalIndices = originalIndices
             )
         }
 
@@ -95,14 +95,14 @@
             it.data.forEachIndexed { index, t ->
                 if (predicate(t)) {
                     data.add(t)
-                    originalIndices.add(it.originalIndices?.get(index) ?: index)
+                    originalIndices.add(it.hintOriginalIndices?.get(index) ?: index)
                 }
             }
             TransformablePage(
-                originalPageOffset = it.originalPageOffset,
+                originalPageOffsets = it.originalPageOffsets,
                 data = data,
-                originalPageSize = it.originalPageSize,
-                originalIndices = originalIndices
+                hintOriginalPageOffset = it.hintOriginalPageOffset,
+                hintOriginalIndices = originalIndices
             )
         }
 
@@ -148,17 +148,26 @@
 
     data class Drop<T : Any>(
         val loadType: LoadType,
-        val count: Int,
+        /**
+         * Smallest [TransformablePage.originalPageOffsets] to drop; inclusive.
+         */
+        val minPageOffset: Int,
+        /**
+         * Largest [TransformablePage.originalPageOffsets] to drop; inclusive
+         */
+        val maxPageOffset: Int,
         val placeholdersRemaining: Int
     ) : PageEvent<T>() {
 
         init {
             require(loadType != REFRESH) { "Drop load type must be PREPEND or APPEND" }
-            require(count >= 0) { "Drop count must be > 0, but was $count" }
+            require(pageCount > 0) { "Drop count must be > 0, but was $pageCount" }
             require(placeholdersRemaining >= 0) {
                 "Invalid placeholdersRemaining $placeholdersRemaining"
             }
         }
+
+        val pageCount get() = maxPageOffset - minPageOffset + 1
     }
 
     data class LoadStateUpdate<T : Any>(
@@ -184,100 +193,3 @@
 
     open suspend fun filter(predicate: suspend (T) -> Boolean): PageEvent<T> = this
 }
-
-private fun <T> MutableList<T>.removeFirst(count: Int) {
-    repeat(count) { removeAt(0) }
-}
-
-private fun <T> MutableList<T>.removeLast(count: Int) {
-    repeat(count) { removeAt(lastIndex) }
-}
-
-internal inline fun <R : Any, T : R, PageStash, Stash> Flow<PageEvent<T>>.scan(
-    crossinline createStash: () -> Stash,
-    crossinline createPageStash: (TransformablePage<T>) -> PageStash,
-    crossinline createInsert: (PageEvent.Insert<T>, List<PageStash>, Stash) -> PageEvent.Insert<R>,
-    crossinline createDrop: (PageEvent.Drop<T>, List<PageStash>, Stash) -> PageEvent.Drop<R>
-): Flow<PageEvent<R>> {
-    var stash: Stash = createStash()
-    val pageStash = mutableListOf<PageStash>()
-    return map { event ->
-        @Suppress("UNCHECKED_CAST")
-        when (event) {
-            is PageEvent.Insert<T> -> {
-                // use the stash before modifying it, since we may want to inspect adjacent pages
-                val output = createInsert(event, pageStash, stash)
-                val pageStashes = event.pages.map { createPageStash(it) }
-                when (event.loadType) {
-                    REFRESH -> {
-                        check(pageStash.isEmpty())
-                        pageStash.addAll(pageStashes)
-                    }
-                    PREPEND -> {
-                        pageStash.addAll(0, pageStashes)
-                    }
-                    APPEND -> {
-                        pageStash.addAll(pageStash.size, pageStashes)
-                    }
-                }
-                output
-            }
-            is PageEvent.Drop -> {
-                if (event.loadType == PREPEND) {
-                    pageStash.removeFirst(event.count)
-                } else {
-                    pageStash.removeLast(event.count)
-                }
-                // use the stash after modifying it
-                createDrop(event, pageStash, stash)
-            }
-            is PageEvent.LoadStateUpdate -> event as PageEvent<R>
-        }
-    }
-}
-
-/**
- * Transforms the Flow to an output-equivalent Flow, which does not have empty pages.
- *
- * This can be used before accessing adjacent pages, to ensure adjacent pages have context in
- * them.
- *
- * Note that we don't drop events, since those can contain other important state
- */
-internal fun <T : Any> Flow<PageEvent<T>>.removeEmptyPages(): Flow<PageEvent<T>> = scan(
-    createStash = { Unit },
-    createPageStash = { page ->
-        // stash contains whether incoming page was empty
-        page.data.isEmpty()
-    },
-    createInsert = { insert, _, _ ->
-        if (insert.pages.any { it.data.isEmpty() }) {
-            // filter out empty pages
-            insert.transformPages { pages -> pages.filter { it.data.isNotEmpty() } }
-        } else {
-            // no empty pages, can safely reuse this page
-            insert
-        }
-    },
-    createDrop = { drop, pageStash, _ ->
-        var newCount = drop.count
-        if (drop.loadType == PREPEND) {
-            repeat(drop.count) { i ->
-                if (pageStash[i]) {
-                    newCount--
-                }
-            }
-        } else {
-            repeat(drop.count) { i ->
-                if (pageStash[pageStash.lastIndex - i]) {
-                    newCount--
-                }
-            }
-        }
-        if (drop.count == newCount) {
-            drop
-        } else {
-            drop.copy(count = newCount)
-        }
-    }
-)
diff --git a/paging/common/src/main/kotlin/androidx/paging/PageFetcherSnapshot.kt b/paging/common/src/main/kotlin/androidx/paging/PageFetcherSnapshot.kt
index 776d7ac..df615df 100644
--- a/paging/common/src/main/kotlin/androidx/paging/PageFetcherSnapshot.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/PageFetcherSnapshot.kt
@@ -22,7 +22,6 @@
 import androidx.paging.LoadType.APPEND
 import androidx.paging.LoadType.PREPEND
 import androidx.paging.LoadType.REFRESH
-import androidx.paging.PageEvent.Drop
 import androidx.paging.PageEvent.LoadStateUpdate
 import androidx.paging.PagingSource.LoadParams
 import androidx.paging.PagingSource.LoadResult
@@ -498,9 +497,9 @@
             }
 
             stateLock.withLock {
-                state.dropInfo(dropType, generationalHint.hint)?.let { info ->
-                    state.drop(dropType, info.pageCount, info.placeholdersRemaining)
-                    pageEventCh.send(Drop(dropType, info.pageCount, info.placeholdersRemaining))
+                state.dropEventOrNull(dropType, generationalHint.hint)?.let { event ->
+                    state.drop(event)
+                    pageEventCh.send(event)
                 }
 
                 loadKey = state.nextLoadKeyOrNull(loadType, generationalHint, itemsLoaded)
diff --git a/paging/common/src/main/kotlin/androidx/paging/PageFetcherSnapshotState.kt b/paging/common/src/main/kotlin/androidx/paging/PageFetcherSnapshotState.kt
index 3a28a06..3f05e26 100644
--- a/paging/common/src/main/kotlin/androidx/paging/PageFetcherSnapshotState.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/PageFetcherSnapshotState.kt
@@ -123,7 +123,7 @@
             PREPEND -> 0 - initialPageIndex
             APPEND -> pages.size - initialPageIndex - 1
         }
-        val pages = listOf(TransformablePage(sourcePageIndex, data, data.size, null))
+        val pages = listOf(TransformablePage(sourcePageIndex, data))
         return when (loadType) {
             REFRESH -> Refresh(
                 pages = pages,
@@ -197,38 +197,43 @@
         return true
     }
 
-    fun drop(loadType: LoadType, pageCount: Int, placeholdersRemaining: Int) {
-        check(pages.size >= pageCount) {
-            "invalid drop count. have ${pages.size} but wanted to drop $pageCount"
+    fun drop(event: PageEvent.Drop<Value>) {
+        check(event.pageCount <= pages.size) {
+            "invalid drop count. have ${pages.size} but wanted to drop ${event.pageCount}"
         }
 
         // Reset load state to NotLoading(endOfPaginationReached = false).
-        failedHintsByLoadType.remove(loadType)
-        loadStates.set(loadType, false, NotLoading.Incomplete)
+        failedHintsByLoadType.remove(event.loadType)
+        loadStates.set(event.loadType, false, NotLoading.Incomplete)
 
-        when (loadType) {
+        when (event.loadType) {
             PREPEND -> {
-                repeat(pageCount) { _pages.removeAt(0) }
-                initialPageIndex -= pageCount
+                repeat(event.pageCount) { _pages.removeAt(0) }
+                initialPageIndex -= event.pageCount
 
-                placeholdersBefore = placeholdersRemaining
+                placeholdersBefore = event.placeholdersRemaining
 
                 prependLoadId++
                 prependLoadIdCh.offer(prependLoadId)
             }
             APPEND -> {
-                repeat(pageCount) { _pages.removeAt(pages.size - 1) }
+                repeat(event.pageCount) { _pages.removeAt(pages.size - 1) }
 
-                placeholdersAfter = placeholdersRemaining
+                placeholdersAfter = event.placeholdersRemaining
 
                 appendLoadId++
                 appendLoadIdCh.offer(appendLoadId)
             }
-            else -> throw IllegalArgumentException("cannot drop $loadType")
+            else -> throw IllegalArgumentException("cannot drop ${event.loadType}")
         }
     }
 
-    fun dropInfo(loadType: LoadType, hint: ViewportHint): DropInfo? {
+    /**
+     * @return [PageEvent.Drop] for [loadType] that would allow this [PageFetcherSnapshotState] to
+     * respect [PagingConfig.maxSize], `null` if no pages should be dropped for the provided
+     * [loadType].
+     */
+    fun dropEventOrNull(loadType: LoadType, hint: ViewportHint): PageEvent.Drop<Value>? {
         if (config.maxSize == MAX_SIZE_UNBOUNDED) return null
         // Never drop below 2 pages as this can cause UI flickering with certain configs and it's
         // much more important to protect against this behaviour over respecting a config where
@@ -237,57 +242,53 @@
 
         if (storageCount <= config.maxSize) return null
 
-        when (loadType) {
-            REFRESH -> throw IllegalArgumentException(
-                "Drop LoadType must be PREPEND or APPEND, but got $loadType"
-            )
-            PREPEND -> {
-                var pageCount = 0
-                var itemsToDrop = 0
-                while (pageCount < pages.size && storageCount - itemsToDrop > config.maxSize) {
-                    val pageSize = pages[pageCount].data.size
-                    val itemsAfterDrop = hint.presentedItemsBefore - itemsToDrop - pageSize
-                    // Do not drop pages that would fulfill prefetchDistance.
-                    if (itemsAfterDrop < config.prefetchDistance) break
+        require(loadType != REFRESH) {
+            "Drop LoadType must be PREPEND or APPEND, but got $loadType"
+        }
 
-                    itemsToDrop += pageSize
-                    pageCount++
-                }
+        // Compute pageCount and itemsToDrop
+        var pagesToDrop = 0
+        var itemsToDrop = 0
+        while (pagesToDrop < pages.size && storageCount - itemsToDrop > config.maxSize) {
+            val pageSize = when (loadType) {
+                PREPEND -> pages[pagesToDrop].data.size
+                else -> pages[pages.lastIndex - pagesToDrop].data.size
+            }
+            val itemsAfterDrop = when (loadType) {
+                PREPEND -> hint.presentedItemsBefore - itemsToDrop - pageSize
+                else -> hint.presentedItemsAfter - itemsToDrop - pageSize
+            }
+            // Do not drop pages that would fulfill prefetchDistance.
+            if (itemsAfterDrop < config.prefetchDistance) break
 
-                val placeholdersRemaining = when {
+            itemsToDrop += pageSize
+            pagesToDrop++
+        }
+
+        return when (pagesToDrop) {
+            0 -> null
+            else -> PageEvent.Drop(
+                loadType = loadType,
+                minPageOffset = when (loadType) {
+                    // originalPageOffset of the first page.
+                    PREPEND -> -initialPageIndex
+                    // maxPageOffset - pagesToDrop; We subtract one from pagesToDrop, since this
+                    // value is inclusive.
+                    else -> pages.lastIndex - initialPageIndex - (pagesToDrop - 1)
+                },
+                maxPageOffset = when (loadType) {
+                    // minPageOffset + pagesToDrop; We subtract on from pagesToDrop, since this
+                    // value is inclusive.
+                    PREPEND -> (pagesToDrop - 1) - initialPageIndex
+                    // originalPageOffset of the last page.
+                    else -> pages.lastIndex - initialPageIndex
+                },
+                placeholdersRemaining = when {
                     !config.enablePlaceholders -> 0
-                    else -> placeholdersBefore + itemsToDrop
-                }
-
-                return when (pageCount) {
-                    0 -> null
-                    else -> DropInfo(pageCount, placeholdersRemaining)
-                }
-            } APPEND -> {
-                var pageCount = 0
-                var itemsToDrop = 0
-                while (pageCount < pages.size && storageCount - itemsToDrop > config.maxSize) {
-                    val pageSize = pages[pages.lastIndex - pageCount].data.size
-                    val itemsAfterDrop = hint.presentedItemsAfter - itemsToDrop - pageSize
-                    // Do not drop pages that would fulfill prefetchDistance.
-                    if (itemsAfterDrop < config.prefetchDistance) break
-
-                    itemsToDrop += pageSize
-                    pageCount++
-                }
-
-                val placeholdersRemaining = when {
-                    !config.enablePlaceholders -> 0
+                    loadType == PREPEND -> placeholdersBefore + itemsToDrop
                     else -> placeholdersAfter + itemsToDrop
                 }
-
-                return when (pageCount) {
-                    0 -> null
-                    else -> DropInfo(pageCount, placeholdersRemaining)
-                }
-            }
+            )
         }
     }
 }
-
-internal class DropInfo(val pageCount: Int, val placeholdersRemaining: Int)
diff --git a/paging/common/src/main/kotlin/androidx/paging/PagePresenter.kt b/paging/common/src/main/kotlin/androidx/paging/PagePresenter.kt
index 26142d3..f0e49eb 100644
--- a/paging/common/src/main/kotlin/androidx/paging/PagePresenter.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/PagePresenter.kt
@@ -31,11 +31,10 @@
     private val pages: MutableList<TransformablePage<T>> = insertEvent.pages.toMutableList()
     override var storageCount: Int = insertEvent.pages.fullCount()
         private set
-
-    val firstPageIndex: Int
-        get() = pages.first().originalPageOffset
-    val lastPageIndex: Int
-        get() = pages.last().originalPageOffset
+    private val originalPageOffsetFirst: Int
+        get() = pages.first().originalPageOffsets.min()!!
+    private val originalPageOffsetLast: Int
+        get() = pages.last().originalPageOffsets.max()!!
     override var placeholdersBefore: Int = insertEvent.placeholdersBefore
         private set
     override var placeholdersAfter: Int = insertEvent.placeholdersAfter
@@ -107,7 +106,7 @@
         }
     }
 
-    fun presenterIndexToHint(index: Int): ViewportHint {
+    fun viewportHintForPresenterIndex(index: Int): ViewportHint {
         var pageIndex = 0
         var indexInPage = index - placeholdersBefore
         while (indexInPage >= pages[pageIndex].data.size && pageIndex < pages.lastIndex) {
@@ -116,19 +115,12 @@
             pageIndex++
         }
 
-        val originalIndices = pages[pageIndex].originalIndices
-        return ViewportHint(
-            pageOffset = pages[pageIndex].originalPageOffset,
-            indexInPage = if (originalIndices != null && indexInPage in originalIndices.indices) {
-                originalIndices[indexInPage]
-            } else {
-                indexInPage
-            },
+        return pages[pageIndex].viewportHintFor(
+            index = indexInPage,
             presentedItemsBefore = index - placeholdersBefore,
             presentedItemsAfter = size - index - placeholdersAfter - 1,
-            originalPageOffsetFirst = firstPageIndex,
-            originalPageOffsetLast = lastPageIndex
-
+            originalPageOffsetFirst = originalPageOffsetFirst,
+            originalPageOffsetLast = originalPageOffsetLast
         )
     }
 
@@ -208,33 +200,67 @@
         }
     }
 
+    /**
+     * @param pageOffsetsToDrop originalPageOffset of pages that were dropped
+     * @return The number of items dropped
+     */
+    private fun dropPagesWithOffsets(pageOffsetsToDrop: IntRange): Int {
+        var removeCount = 0
+        val pageIterator = pages.iterator()
+        while (pageIterator.hasNext()) {
+            val page = pageIterator.next()
+            if (page.originalPageOffsets.any { pageOffsetsToDrop.contains(it) }) {
+                removeCount += page.data.size
+                pageIterator.remove()
+            }
+        }
+
+        return removeCount
+    }
+
+    /**
+     * Helper which converts a [PageEvent.Drop] to a set of [ProcessPageEventCallback] events by
+     * dropping all pages that depend on the n-lowest or n-highest originalPageOffsets.
+     *
+     * Note: We never run DiffUtil here because it is safe to assume that empty pages can never
+     * become non-empty no matter what transformations they go through. [ProcessPageEventCallback]
+     * events generated by this helper always drop contiguous sets of items because pages that
+     * depend on multiple originalPageOffsets will always be the next closest page that's non-empty.
+     */
     private fun dropPages(drop: PageEvent.Drop<T>, callback: ProcessPageEventCallback) {
         val oldSize = size
+
         if (drop.loadType == PREPEND) {
-            val removeCount = pages.take(drop.count).fullCount()
-
-            val placeholdersChangedCount = minOf(drop.placeholdersRemaining, removeCount)
-            val placeholdersChangedPos = placeholdersBefore + removeCount -
-                    placeholdersChangedCount
-
-            val itemsRemovedCount = removeCount - placeholdersChangedCount
-            val itemsRemovedPos = 0
+            val oldPlaceholdersBefore = placeholdersBefore
 
             // first update all state...
-            for (i in 0 until drop.count) {
-                pages.removeAt(0)
-            }
-            storageCount -= removeCount
+            val itemDropCount = dropPagesWithOffsets(drop.minPageOffset..drop.maxPageOffset)
+            storageCount -= itemDropCount
             placeholdersBefore = drop.placeholdersRemaining
 
             // ... then trigger callbacks, so callbacks won't see inconsistent state
-            callback.onChanged(placeholdersChangedPos, placeholdersChangedCount)
-            callback.onRemoved(itemsRemovedPos, itemsRemovedCount)
-            val placeholderInsertedCount = size - oldSize + itemsRemovedCount
-            if (placeholderInsertedCount > 0) {
-                callback.onInserted(0, placeholderInsertedCount)
-            } else if (placeholderInsertedCount < 0) {
-                callback.onRemoved(0, -placeholderInsertedCount)
+            // Trim or insert to expected size.
+            val expectedSize = size
+            val placeholdersToInsert = expectedSize - oldSize
+            if (placeholdersToInsert > 0) {
+                callback.onInserted(0, placeholdersToInsert)
+            } else if (placeholdersToInsert < 0) {
+                callback.onRemoved(0, -placeholdersToInsert)
+            }
+
+            // Compute the index of the first item that must be rebound as a placeholder.
+            // If any placeholders were inserted above, we only need to send onChanged for the next
+            // n = (drop.placeholdersRemaining - placeholdersToInsert) items. E.g., if two nulls
+            // were inserted above, then the onChanged event can start from index = 2.
+            // Note: In cases where more items were dropped than there were previously placeholders,
+            // we can simply rebind n = drop.placeholdersRemaining items starting from position = 0.
+            val firstItemIndex = maxOf(0, oldPlaceholdersBefore + placeholdersToInsert)
+            // Compute the number of previously loaded items that were dropped and now need to be
+            // updated to null. This computes the distance between firstItemIndex (inclusive),
+            // and index of the last leading placeholder (inclusive) in the final list.
+            val changeCount = drop.placeholdersRemaining - firstItemIndex
+            if (changeCount > 0) {
+                callback.onChanged(firstItemIndex, changeCount)
             }
 
             // Dropping from prepend direction implies NotLoading(endOfPaginationReached = false).
@@ -244,29 +270,40 @@
                 loadState = NotLoading.Incomplete
             )
         } else {
-            val removeCount = pages.takeLast(drop.count).fullCount()
-
-            val placeholdersChangedCount = minOf(drop.placeholdersRemaining, removeCount)
-            val placeholdersChangedPos = placeholdersBefore + storageCount - removeCount
-
-            val itemsRemovedCount = removeCount - placeholdersChangedCount
-            val itemsRemovedPos = placeholdersChangedPos + placeholdersChangedCount
+            val oldPlaceholdersAfter = placeholdersAfter
 
             // first update all state...
-            for (i in 0 until drop.count) {
-                pages.removeAt(pages.lastIndex)
-            }
-            storageCount -= removeCount
+            val itemDropCount = dropPagesWithOffsets(drop.minPageOffset..drop.maxPageOffset)
+            storageCount -= itemDropCount
             placeholdersAfter = drop.placeholdersRemaining
 
             // ... then trigger callbacks, so callbacks won't see inconsistent state
-            callback.onChanged(placeholdersChangedPos, placeholdersChangedCount)
-            callback.onRemoved(itemsRemovedPos, itemsRemovedCount)
-            val placeholderInsertedCount = size - oldSize + itemsRemovedCount
-            if (placeholderInsertedCount > 0) {
-                callback.onInserted(size, placeholderInsertedCount)
-            } else if (placeholderInsertedCount < 0) {
-                callback.onRemoved(size, -placeholderInsertedCount)
+            // Trim or insert to expected size.
+            val expectedSize = size
+            val placeholdersToInsert = expectedSize - oldSize
+            if (placeholdersToInsert > 0) {
+                callback.onInserted(oldSize, placeholdersToInsert)
+            } else if (placeholdersToInsert < 0) {
+                callback.onRemoved(oldSize + placeholdersToInsert, -placeholdersToInsert)
+            }
+
+            // Number of trailing placeholders in the list, before dropping, that were removed
+            // above during size adjustment.
+            val oldPlaceholdersRemoved = when {
+                placeholdersToInsert < 0 -> minOf(oldPlaceholdersAfter, -placeholdersToInsert)
+                else -> 0
+            }
+            // Compute the number of previously loaded items that were dropped and now need to be
+            // updated to null. This subtracts the total number of existing placeholders in the
+            // list, before dropping, that were not removed above during size adjustment, from
+            // the total number of expected placeholders.
+            val changeCount =
+                drop.placeholdersRemaining - (oldPlaceholdersAfter - oldPlaceholdersRemoved)
+            if (changeCount > 0) {
+                callback.onChanged(
+                    position = size - drop.placeholdersRemaining,
+                    count = changeCount
+                )
             }
 
             // Dropping from append direction implies NotLoading(endOfPaginationReached = false).
diff --git a/paging/common/src/main/kotlin/androidx/paging/PagingDataDiffer.kt b/paging/common/src/main/kotlin/androidx/paging/PagingDataDiffer.kt
index 975bd64..746fefc 100644
--- a/paging/common/src/main/kotlin/androidx/paging/PagingDataDiffer.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/PagingDataDiffer.kt
@@ -121,6 +121,8 @@
     suspend fun collectFrom(pagingData: PagingData<T>) = collectFromRunner.runInIsolation {
         receiver = pagingData.receiver
 
+        // TODO: Validate only empty pages between separator pages and its dependent
+        //  pages.
         pagingData.flow.collect { event ->
             withContext<Unit>(mainDispatcher) {
                 if (event is PageEvent.Insert && event.loadType == REFRESH) {
@@ -151,7 +153,9 @@
                     // list.
                     transformedLastAccessedIndex?.let { newIndex ->
                         lastAccessedIndex = newIndex
-                        receiver?.accessHint(newPresenter.presenterIndexToHint(newIndex))
+                        receiver?.accessHint(
+                            newPresenter.viewportHintForPresenterIndex(newIndex)
+                        )
                     }
                 } else {
                     if (postEvents()) {
@@ -170,7 +174,8 @@
                     // If index points to a placeholder after transformations, resend it unless
                     // there are no more items to load.
                     if (event is PageEvent.Insert) {
-                        val prependDone = event.combinedLoadStates.prepend.endOfPaginationReached
+                        val prependDone =
+                            event.combinedLoadStates.prepend.endOfPaginationReached
                         val appendDone = event.combinedLoadStates.append.endOfPaginationReached
                         val canContinueLoading = !(event.loadType == PREPEND && prependDone) &&
                                 !(event.loadType == APPEND && appendDone)
@@ -187,7 +192,7 @@
 
                             if (shouldResendHint) {
                                 receiver?.accessHint(
-                                    presenter.presenterIndexToHint(lastAccessedIndex)
+                                    presenter.viewportHintForPresenterIndex(lastAccessedIndex)
                                 )
                             } else {
                                 // lastIndex fulfilled, so reset lastAccessedIndexUnfulfilled.
@@ -211,7 +216,7 @@
         lastAccessedIndexUnfulfilled = true
         lastAccessedIndex = index
 
-        receiver?.accessHint(presenter.presenterIndexToHint(index))
+        receiver?.accessHint(presenter.viewportHintForPresenterIndex(index))
         return presenter.get(index)
     }
 
diff --git a/paging/common/src/main/kotlin/androidx/paging/Separators.kt b/paging/common/src/main/kotlin/androidx/paging/Separators.kt
index 5dda66d..5154f5b 100644
--- a/paging/common/src/main/kotlin/androidx/paging/Separators.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/Separators.kt
@@ -41,7 +41,7 @@
     val outputIndices = ArrayList<Int>(initialCapacity)
 
     outputList.add(data.first())
-    outputIndices.add(originalIndices?.first() ?: 0)
+    outputIndices.add(hintOriginalIndices?.first() ?: 0)
     for (i in 1 until data.size) {
         val item = data[i]
         val separator = generator(data[i - 1], item)
@@ -63,138 +63,100 @@
         this as TransformablePage<R>
     } else {
         TransformablePage(
-            originalPageOffset = originalPageOffset,
+            originalPageOffsets = originalPageOffsets,
             data = outputList,
-            originalPageSize = originalPageSize,
-            originalIndices = outputIndices
+            hintOriginalPageOffset = hintOriginalPageOffset,
+            hintOriginalIndices = outputIndices
         )
     }
 }
 
 /**
- * Create a TransformablePage with the given separator (or empty, if the separator is null)
+ * Create a [TransformablePage] with the given separator (or empty, if the separator is null)
  */
 internal fun <T : Any> separatorPage(
+    separator: T,
+    originalPageOffsets: IntArray,
+    hintOriginalPageOffset: Int,
+    hintOriginalIndex: Int
+): TransformablePage<T> = TransformablePage(
+    originalPageOffsets = originalPageOffsets,
+    data = listOf(separator),
+    hintOriginalPageOffset = hintOriginalPageOffset,
+    hintOriginalIndices = listOf(hintOriginalIndex)
+)
+
+/**
+ * Create a [TransformablePage] with the given separator, and add it if [separator] is non-null
+ *
+ * This is a helper to create separator pages that contain a single separator to be used to join
+ * pages provided from stream.
+ */
+internal fun <T : Any> MutableList<TransformablePage<T>>.addSeparatorPage(
     separator: T?,
-    originalPageOffset: Int,
-    originalPageSize: Int,
-    originalIndex: Int
-): TransformablePage<T> = if (separator != null) {
-    // page with just the separator
-    TransformablePage(
-        originalPageOffset = originalPageOffset,
-        data = listOf(separator),
-        originalPageSize = originalPageSize,
-        originalIndices = listOf(originalIndex)
-    )
-} else {
-    // empty page
-    TransformablePage(
-        originalPageOffset = originalPageOffset,
-        data = emptyList(),
-        originalPageSize = originalPageSize,
-        originalIndices = null
-    )
-}
-
-/**
- * Create a TransformablePage with the given separator (or empty, if the separator is null)
- */
-internal fun <R : Any, T : R> separatorPage(
-    separator: R?,
-    adjacentPage: TransformablePage<T>,
-    originalIndex: Int
-): TransformablePage<R> = separatorPage(
-    separator = separator,
-    originalPageOffset = adjacentPage.originalPageOffset,
-    originalPageSize = adjacentPage.originalPageSize,
-    originalIndex = originalIndex
-)
-
-/**
- * Create a TransformablePage with the given separator (or empty, if the separator is null)
- */
-internal fun <R : Any, T : R> separatorPage(
-    separator: R?,
-    adjacentPage: DataPage<T>,
-    originalIndex: Int
-): TransformablePage<R> = separatorPage(
-    separator = separator,
-    originalPageOffset = adjacentPage.originalPageOffset,
-    originalPageSize = adjacentPage.originalPageSize,
-    originalIndex = originalIndex
-)
-
-/**
- * Per-page adjacency info - used to create adjacent separators.
- */
-internal class DataPage<T : Any>(
-    page: TransformablePage<T>
+    originalPageOffsets: IntArray,
+    hintOriginalPageOffset: Int,
+    hintOriginalIndex: Int
 ) {
-    val first: T = page.data.first()
-    val last: T = page.data.last()
-    val originalPageOffset: Int = page.originalPageOffset
-    val originalPageSize: Int = page.originalPageSize
-    val originalLastIndex
-        get() = originalPageSize - 1
+    if (separator == null) return
+
+    val separatorPage = separatorPage(
+        separator = separator,
+        originalPageOffsets = originalPageOffsets,
+        hintOriginalPageOffset = hintOriginalPageOffset,
+        hintOriginalIndex = hintOriginalIndex
+    )
+    add(separatorPage)
 }
 
 /**
- * Iterate through the list of page info, dropping events, and mapping the incoming drop count to
- * an output count.
+ * Create a [TransformablePage] with the given separator, and add it if [separator] is non-null
+ *
+ * This is a helper to create separator pages that contain a single separator to be used to join
+ * pages provided from stream.
  */
-private inline fun <T : Any> MutableList<DataPage<T>?>.dropPages(
-    nonSeparatorCount: Int,
-    indexProvider: (List<DataPage<T>?>) -> Int
-): Int {
-    if (isEmpty()) {
-        // nothing to drop
-        check(nonSeparatorCount == 0)
-        return 0
-    }
-
-    // drop nonSeparatorCount of pages. Even if 0, we want to be sure to drop a terminal
-    // separator, since that side of the list is no longer done
-    var nonSeparatorPagesToDrop = nonSeparatorCount
-    var outputDropCount = 0
-    while (nonSeparatorPagesToDrop > 0) {
-        val page = removeAt(indexProvider(this))
-        if (page != null) {
-            nonSeparatorPagesToDrop--
-        }
-        outputDropCount++
-    }
-
-    // now check if last page is a separator. if so, we need to drop it too, since it was built
-    // with now-dropped data (unless nonSeparatorCount was 0, which is why the early return)
-    val finalPotentialSeparatorIndex = indexProvider(this)
-    val finalPage = get(finalPotentialSeparatorIndex)
-    if (finalPage == null) {
-        removeAt(finalPotentialSeparatorIndex)
-        outputDropCount++
-    }
-    return outputDropCount
+internal fun <R : Any, T : R> MutableList<TransformablePage<R>>.addSeparatorPage(
+    separator: R?,
+    adjacentPageBefore: TransformablePage<T>?,
+    adjacentPageAfter: TransformablePage<T>?,
+    hintOriginalPageOffset: Int,
+    hintOriginalIndex: Int
+) {
+    val beforeOffsets = adjacentPageBefore?.originalPageOffsets
+    val afterOffsets = adjacentPageAfter?.originalPageOffsets
+    addSeparatorPage(
+        separator = separator,
+        originalPageOffsets = when {
+            beforeOffsets != null && afterOffsets != null -> {
+                (beforeOffsets + afterOffsets).distinct().sorted().toIntArray()
+            }
+            beforeOffsets == null && afterOffsets != null -> afterOffsets
+            beforeOffsets != null && afterOffsets == null -> beforeOffsets
+            else -> throw IllegalArgumentException(
+                "Separator page expected adjacentPageBefore or adjacentPageAfter, but both were" +
+                        " null."
+            )
+        },
+        hintOriginalPageOffset = hintOriginalPageOffset,
+        hintOriginalIndex = hintOriginalIndex
+    )
 }
 
-internal fun <T : Any> MutableList<DataPage<T>?>.dropPagesStart(count: Int): Int =
-    dropPages(count) { 0 }
-
-internal fun <T : Any> MutableList<DataPage<T>?>.dropPagesEnd(count: Int): Int =
-    dropPages(count) { it.lastIndex }
-
 private class SeparatorState<R : Any, T : R>(
-    val generator: suspend (T?, T?) -> R?
+    val generator: suspend (before: T?, after: T?) -> R?
 ) {
     /**
-     * Lookup table of previously emitted pages.
+     * Lookup table of previously emitted pages, that skips empty pages.
      *
-     *     Separator -> null
-     *     Non-separator -> DataPage
+     * This table is used to keep track of originalPageOffsets for separators that would span
+     * across empty pages. It includes a simplified version of loaded pages which only has the
+     * first and last item in each page to reduce memory pressure.
      *
-     * This table is used to emit drops (so we know how much to pad drops to account for separators)
-     * and to provide adjacency data, to insert separators as new pages arrive.
+     * Note: [TransformablePage] added to this stash must always have
+     * [TransformablePage.originalPageOffsets] defined, since it needs to keep track of the
+     * originalPageOffset of the last item.
      */
-    val pageStash = mutableListOf<DataPage<T>?>()
+    val pageStash = mutableListOf<TransformablePage<T>>()
 
     /**
      * True if next insert event should be treated as terminal, as a previous terminal event was
@@ -203,10 +165,16 @@
     var endTerminalSeparatorDeferred = false
     var startTerminalSeparatorDeferred = false
 
+    var footerAdded = false
+    var headerAdded = false
+
     @Suppress("UNCHECKED_CAST")
     suspend fun onEvent(event: PageEvent<T>): PageEvent<R> = when (event) {
         is Insert<T> -> onInsert(event)
-        is Drop -> onDrop(event) as Drop<R>
+        is Drop -> {
+            onDrop(event) // Update pageStash state
+            event as Drop<R>
+        }
         is PageEvent.LoadStateUpdate -> event as PageEvent<R>
     }.also {
         // validate internal state after each modification
@@ -248,15 +216,13 @@
     suspend fun onInsert(event: Insert<T>): Insert<R> {
         val eventTerminatesStart = event.terminatesStart()
         val eventTerminatesEnd = event.terminatesEnd()
-        val eventEmpty = event.pages.isEmpty()
+        val eventEmpty = event.pages.all { it.data.isEmpty() }
 
-        if (pageStash.isNotEmpty()) {
-            require(pageStash.first() != null || event.loadType != PREPEND) {
-                "Additional prepend event after prepend state is done"
-            }
-            require(pageStash.last() != null || event.loadType != APPEND) {
-                "Additional append event after append state is done"
-            }
+        require(!headerAdded || event.loadType != PREPEND) {
+            "Additional prepend event after prepend state is done"
+        }
+        require(!footerAdded || event.loadType != APPEND) {
+            "Additional append event after append state is done"
         }
 
         if (eventEmpty) {
@@ -265,8 +231,13 @@
                 val separator = generator(null, null)
                 endTerminalSeparatorDeferred = false
                 startTerminalSeparatorDeferred = false
-                pageStash.add(null) // represents separator
-                return event.transformPages { listOf(separatorPage(separator, 0, 0, 0)) }
+                return if (separator == null) {
+                    event.asRType()
+                } else {
+                    event.transformPages {
+                        listOf(separatorPage(separator, intArrayOf(0), 0, 0))
+                    }
+                }
             } else if (!eventTerminatesStart && !eventTerminatesEnd) {
                 // If event is non terminal simply ignore it.
                 return event.asRType()
@@ -285,95 +256,187 @@
         // If we've gotten to this point, that means the outgoing insert will have data.
         // Either this event has data, or the pageStash does.
         val outList = ArrayList<TransformablePage<R>>(event.pages.size)
-        val outStateList = ArrayList<DataPage<T>?>(event.pages.size)
-        if (eventTerminatesStart) {
-            outStateList.add(null) // represents separator
-            if (eventEmpty) {
-                // header separator, using data from previous generation
-                val firstStash = pageStash.first()!!
-                val separator = generator(null, firstStash.first)
-                outList.add(separatorPage(separator, firstStash, originalIndex = 0))
-            } else {
-                val firstPage = event.pages.first()
-                val separator = generator(null, firstPage.data.first())
-                outList.add(separatorPage(separator, firstPage, originalIndex = 0))
+        val stashOutList = ArrayList<TransformablePage<T>>(event.pages.size)
+
+        var firstNonEmptyPage: TransformablePage<T>? = null
+        var firstNonEmptyPageIndex: Int? = null
+        var lastNonEmptyPage: TransformablePage<T>? = null
+        var lastNonEmptyPageIndex: Int? = null
+        if (!eventEmpty) {
+            // Compute the first non-empty page index to be used as adjacent pages for creating
+            // separator pages.
+            // Note: We're guaranteed to have at least one non-empty page at this point.
+            var pageIndex = 0
+            while (pageIndex < event.pages.lastIndex && event.pages[pageIndex].data.isEmpty()) {
+                pageIndex++
             }
+            firstNonEmptyPageIndex = pageIndex
+            firstNonEmptyPage = event.pages[pageIndex]
+
+            // Compute the last non-empty page index to be used as adjacent pages for creating separator
+            // pages.
+            // Note: We're guaranteed to have at least one non-empty page at this point.
+            pageIndex = event.pages.lastIndex
+            while (pageIndex > 0 && event.pages[pageIndex].data.isEmpty()) {
+                pageIndex--
+            }
+            lastNonEmptyPageIndex = pageIndex
+            lastNonEmptyPage = event.pages[pageIndex]
         }
 
-        // create pages based on data in the event
+        // Header separator
+        if (eventTerminatesStart) {
+            headerAdded = true
+
+            // Using data from previous generation if event is empty, adjacent page otherwise.
+            val pageAfter = if (eventEmpty) pageStash.first() else firstNonEmptyPage!!
+            outList.addSeparatorPage(
+                separator = generator(null, pageAfter.data.first()),
+                adjacentPageBefore = null,
+                adjacentPageAfter = pageAfter,
+                hintOriginalPageOffset = pageAfter.hintOriginalPageOffset,
+                hintOriginalIndex = pageAfter.hintOriginalIndices?.first() ?: 0
+            )
+        }
+
+        // Create pages based on data in the event
         if (!eventEmpty) {
-            var itemBefore = if (event.loadType == APPEND) pageStash.lastOrNull()?.last else null
-            event.pages.forEachIndexed { index, page ->
-                // If page is being appended, or if we're in between pages, insert separator page
-                if (index != 0 || (event.loadType == APPEND && pageStash.isNotEmpty())) {
-                    val separator = generator(itemBefore!!, page.data.first())
-                    outStateList.add(null) // represents separator
-                    outList.add(separatorPage(separator, page, originalIndex = 0))
+            // Add empty pages before [firstNonEmptyPageIndex] from event directly.
+            for (pageIndex in 0 until firstNonEmptyPageIndex!!) {
+                outList.add(event.pages[pageIndex].insertInternalSeparators(generator))
+            }
+
+            // Insert separator page between last stash and first non-empty event page if APPEND.
+            if (event.loadType == APPEND && pageStash.isNotEmpty()) {
+                val lastStash = pageStash.last()
+                val separator = generator(lastStash.data.last(), firstNonEmptyPage!!.data.first())
+                outList.addSeparatorPage(
+                    separator = separator,
+                    adjacentPageBefore = lastStash,
+                    adjacentPageAfter = firstNonEmptyPage,
+                    hintOriginalPageOffset = firstNonEmptyPage.hintOriginalPageOffset,
+                    hintOriginalIndex = firstNonEmptyPage.hintOriginalIndices?.first() ?: 0
+                )
+            }
+
+            // Add the first non-empty insert event page with separators inserted.
+            stashOutList.add(transformablePageToStash(firstNonEmptyPage!!))
+            outList.add(firstNonEmptyPage.insertInternalSeparators(generator))
+
+            // Handle event pages that may be sparsely populated by empty pages.
+            event.pages
+                .subList(firstNonEmptyPageIndex, lastNonEmptyPageIndex!! + 1)
+                // Note: If we enter reduce loop, pageBefore is guaranteed to be non-null.
+                .reduce { pageBefore, page ->
+                    if (page.data.isNotEmpty()) {
+                        // Insert separator pages in between insert event pages.
+                        val separator = generator(pageBefore.data.last(), page.data.first())
+                        outList.addSeparatorPage(
+                            separator = separator,
+                            adjacentPageBefore = pageBefore,
+                            adjacentPageAfter = page,
+                            hintOriginalPageOffset = if (event.loadType == PREPEND) {
+                                pageBefore.hintOriginalPageOffset
+                            } else {
+                                page.hintOriginalPageOffset
+                            },
+                            hintOriginalIndex = if (event.loadType == PREPEND) {
+                                pageBefore.hintOriginalIndices?.last() ?: pageBefore.data.lastIndex
+                            } else {
+                                page.hintOriginalIndices?.first() ?: 0
+                            }
+                        )
+                    }
+
+                    if (page.data.isNotEmpty()) {
+                        stashOutList.add(transformablePageToStash(page))
+                    }
+                    // Add the insert event page with separators inserted.
+                    outList.add(page.insertInternalSeparators(generator))
+
+                    // Current page becomes the next pageBefore on next iteration unless empty.
+                    if (page.data.isNotEmpty()) page else pageBefore
                 }
 
-                outStateList.add(DataPage(page))
-                outList.add(page.insertInternalSeparators(generator))
-
-                itemBefore = page.data.last()
-            }
+            // Insert separator page between first stash and last non-empty event page if PREPEND.
             if (event.loadType == PREPEND && pageStash.isNotEmpty()) {
-                val lastPage = event.pages.last()
-                val separator = generator(lastPage.data.last(), pageStash.first()!!.first)
-                outStateList.add(null) // represents separator
-                outList.add(
-                    separatorPage(separator, lastPage, lastPage.originalLastIndex)
+                val pageAfter = pageStash.first()
+                val separator = generator(lastNonEmptyPage!!.data.last(), pageAfter.data.first())
+                outList.addSeparatorPage(
+                    separator = separator,
+                    adjacentPageBefore = lastNonEmptyPage,
+                    adjacentPageAfter = pageAfter,
+                    hintOriginalPageOffset = lastNonEmptyPage.hintOriginalPageOffset,
+                    hintOriginalIndex = lastNonEmptyPage.hintOriginalIndices?.last()
+                        ?: lastNonEmptyPage.data.lastIndex
                 )
             }
+
+            // Add empty pages after [lastNonEmptyPageIndex] from event directly.
+            for (pageIndex in (lastNonEmptyPageIndex + 1)..event.pages.lastIndex) {
+                outList.add(event.pages[pageIndex].insertInternalSeparators(generator))
+            }
         }
 
+        // Footer separator
         if (eventTerminatesEnd) {
-            outStateList.add(null) // represents separator
-            if (eventEmpty) {
-                val lastStash = pageStash.last()!!
-                // header separator, using data from previous generation
-                val separator = generator(lastStash.last, null)
-                outList.add(separatorPage(separator, lastStash, lastStash.originalLastIndex))
-            } else {
-                // header separator, using data from adjacent page
-                val lastPage = event.pages.last()
-                val separator = generator(lastPage.data.first(), null)
-                outList.add(
-                    separatorPage(separator, lastPage, lastPage.originalLastIndex)
-                )
-            }
+            footerAdded = true
+
+            // Using data from previous generation if event is empty, adjacent page otherwise.
+            val pageBefore = if (eventEmpty) pageStash.last() else lastNonEmptyPage!!
+            outList.addSeparatorPage(
+                separator = generator(pageBefore.data.last(), null),
+                adjacentPageBefore = pageBefore,
+                adjacentPageAfter = null,
+                hintOriginalPageOffset = pageBefore.hintOriginalPageOffset,
+                hintOriginalIndex = pageBefore.hintOriginalIndices?.last()
+                    ?: pageBefore.data.lastIndex
+            )
         }
 
         endTerminalSeparatorDeferred = false
         startTerminalSeparatorDeferred = false
 
         if (event.loadType == APPEND) {
-            pageStash.addAll(outStateList)
+            pageStash.addAll(stashOutList)
         } else {
-            pageStash.addAll(0, outStateList)
+            pageStash.addAll(0, stashOutList)
         }
         return event.transformPages { outList }
     }
 
-    fun onDrop(event: Drop<T>): Drop<T> {
-        val newCount = if (event.loadType == PREPEND) {
-            if (pageStash.isEmpty()) {
+    /**
+     * Process a [Drop] event to update [pageStash] stage.
+     */
+    fun onDrop(event: Drop<T>) {
+        if (pageStash.isEmpty()) {
+            if (event.loadType == PREPEND) {
                 startTerminalSeparatorDeferred = false
-            }
-            pageStash.dropPagesStart(event.count)
-        } else {
-            if (pageStash.isEmpty()) {
+            } else {
                 endTerminalSeparatorDeferred = false
             }
-            pageStash.dropPagesEnd(event.count)
         }
 
-        @Suppress("UNCHECKED_CAST")
-        return if (newCount == event.count) {
-            event
-        } else {
-            event.copy(count = newCount)
+        // Drop all stashes that depend on pageOffset being dropped.
+        val pageOffsetsToDrop = event.minPageOffset..event.maxPageOffset
+        pageStash.removeAll { stash ->
+            stash.originalPageOffsets.any { pageOffsetsToDrop.contains(it) }
         }
     }
+
+    private fun <T : Any> transformablePageToStash(
+        originalPage: TransformablePage<T>
+    ): TransformablePage<T> {
+        return TransformablePage(
+            originalPageOffsets = originalPage.originalPageOffsets,
+            data = listOf(originalPage.data.first(), originalPage.data.last()),
+            hintOriginalPageOffset = originalPage.hintOriginalPageOffset,
+            hintOriginalIndices = listOf(
+                originalPage.hintOriginalIndices?.first() ?: 0,
+                originalPage.hintOriginalIndices?.last() ?: originalPage.data.lastIndex
+            )
+        )
+    }
 }
 
 /**
@@ -384,5 +447,5 @@
     generator: suspend (T?, T?) -> R?
 ): Flow<PageEvent<R>> {
     val separatorState = SeparatorState { before: T?, after: T? -> generator(before, after) }
-    return removeEmptyPages().map { separatorState.onEvent(it) }
+    return map { separatorState.onEvent(it) }
 }
\ No newline at end of file
diff --git a/paging/common/src/main/kotlin/androidx/paging/TransformablePage.kt b/paging/common/src/main/kotlin/androidx/paging/TransformablePage.kt
index f091909..a4e378c 100644
--- a/paging/common/src/main/kotlin/androidx/paging/TransformablePage.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/TransformablePage.kt
@@ -18,9 +18,12 @@
 
 internal data class TransformablePage<T : Any>(
     /**
-     * Index of the original page (pre-transformation) relative to initial load = 0
+     * List of original (pre-transformation) page offsets the original page relative to initial = 0,
+     * that this [TransformablePage] depends on.
+     *
+     * This array is always sorted.
      */
-    val originalPageOffset: Int,
+    val originalPageOffsets: IntArray,
 
     /**
      * Data to present (post-transformation)
@@ -28,9 +31,10 @@
     val data: List<T>,
 
     /**
-     * Size of the original page (pre-transformation)
+     * Original (pre-transformation) page offset relative to initial = 0, that [hintOriginalIndices]
+     * are associated with.
      */
-    val originalPageSize: Int,
+    val hintOriginalPageOffset: Int,
 
     /**
      * Optional lookup table for page indices.
@@ -39,24 +43,73 @@
      * pre-transformation index.
      *
      * If null, the indices of [data] map directly to their original pre-transformation index.
+     *
+     * Note: [hintOriginalIndices] refers to indices of the original item which can be found in the
+     * loaded pages with pageOffset == [hintOriginalPageOffset].
      */
-    val originalIndices: List<Int>?
+    val hintOriginalIndices: List<Int>?
 ) {
     /**
      * Simple constructor for creating pre-transformation pages, which don't need an index lookup
+     * and only reference a single [originalPageOffset].
      */
     constructor(
         originalPageOffset: Int,
         data: List<T>
-    ) : this(originalPageOffset, data, data.size, null)
+    ) : this(intArrayOf(originalPageOffset), data, originalPageOffset, null)
 
     init {
-        require(originalIndices == null || originalIndices.size == data.size) {
-            "If originalIndices (size = ${originalIndices!!.size}) is provided," +
+        require(originalPageOffsets.isNotEmpty()) {
+            "originalPageOffsets cannot be empty when constructing TransformablePage"
+        }
+
+        require(hintOriginalIndices == null || hintOriginalIndices.size == data.size) {
+            "If originalIndices (size = ${hintOriginalIndices!!.size}) is provided," +
                     " it must be same length as data (size = ${data.size})"
         }
     }
 
-    val originalLastIndex
-        get() = originalPageSize - 1
+    fun viewportHintFor(
+        index: Int,
+        presentedItemsBefore: Int,
+        presentedItemsAfter: Int,
+        originalPageOffsetFirst: Int,
+        originalPageOffsetLast: Int
+    ) = ViewportHint(
+        pageOffset = hintOriginalPageOffset,
+        indexInPage = when {
+            hintOriginalIndices?.indices?.contains(index) == true -> hintOriginalIndices[index]
+            else -> index
+        },
+        presentedItemsBefore = presentedItemsBefore,
+        presentedItemsAfter = presentedItemsAfter,
+        originalPageOffsetFirst = originalPageOffsetFirst,
+        originalPageOffsetLast = originalPageOffsetLast
+    )
+
+    // Do not edit. Implementation generated by Studio, since data class uses referential equality
+    // for IntArray.
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (javaClass != other?.javaClass) return false
+
+        other as TransformablePage<*>
+
+        if (!originalPageOffsets.contentEquals(other.originalPageOffsets)) return false
+        if (data != other.data) return false
+        if (hintOriginalPageOffset != other.hintOriginalPageOffset) return false
+        if (hintOriginalIndices != other.hintOriginalIndices) return false
+
+        return true
+    }
+
+    // Do not edit. Implementation generated by Studio, since data class uses referential  equality
+    // for IntArray.
+    override fun hashCode(): Int {
+        var result = originalPageOffsets.contentHashCode()
+        result = 31 * result + data.hashCode()
+        result = 31 * result + hintOriginalPageOffset
+        result = 31 * result + (hintOriginalIndices?.hashCode() ?: 0)
+        return result
+    }
 }
diff --git a/paging/common/src/test/kotlin/androidx/paging/CachingTest.kt b/paging/common/src/test/kotlin/androidx/paging/CachingTest.kt
index 0ad5c801..39223c1 100644
--- a/paging/common/src/test/kotlin/androidx/paging/CachingTest.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/CachingTest.kt
@@ -325,8 +325,8 @@
                             indexInPage = it.pages.last().data.size - 1,
                             presentedItemsBefore = 0,
                             presentedItemsAfter = 0,
-                            originalPageOffsetFirst = it.pages.first().originalPageOffset,
-                            originalPageOffsetLast = it.pages.last().originalPageOffset
+                            originalPageOffsetFirst = it.pages.first().originalPageOffsets.min()!!,
+                            originalPageOffsetLast = it.pages.last().originalPageOffsets.max()!!
                         )
                     )
                 } else {
diff --git a/paging/common/src/test/kotlin/androidx/paging/FlattenedPageEventStorageTest.kt b/paging/common/src/test/kotlin/androidx/paging/FlattenedPageEventStorageTest.kt
index f088e92..99d2f91 100644
--- a/paging/common/src/test/kotlin/androidx/paging/FlattenedPageEventStorageTest.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/FlattenedPageEventStorageTest.kt
@@ -176,7 +176,8 @@
         list.add(
             Drop(
                 loadType = PREPEND,
-                count = 1,
+                minPageOffset = 0,
+                maxPageOffset = 0,
                 placeholdersRemaining = 6
             )
         )
@@ -212,7 +213,8 @@
         list.add(
             Drop(
                 loadType = APPEND,
-                count = 1,
+                minPageOffset = 1,
+                maxPageOffset = 1,
                 placeholdersRemaining = 7
             )
         )
diff --git a/paging/common/src/test/kotlin/androidx/paging/HeaderFooterTest.kt b/paging/common/src/test/kotlin/androidx/paging/HeaderFooterTest.kt
index 7aa8693..85bac00 100644
--- a/paging/common/src/test/kotlin/androidx/paging/HeaderFooterTest.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/HeaderFooterTest.kt
@@ -14,14 +14,14 @@
  * limitations under the License.
  */
 
-@file:OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
-
 package androidx.paging
 
 import androidx.paging.LoadState.NotLoading
 import androidx.paging.PageEvent.Insert.Companion.Append
 import androidx.paging.PageEvent.Insert.Companion.Prepend
 import androidx.paging.PageEvent.Insert.Companion.Refresh
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.single
 import kotlinx.coroutines.test.runBlockingTest
@@ -38,6 +38,7 @@
     appendLocal = NotLoading.Complete
 )
 
+@OptIn(ExperimentalCoroutinesApi::class)
 @RunWith(JUnit4::class)
 class HeaderFooterTest {
 
@@ -62,9 +63,7 @@
             pages = listOf(
                 TransformablePage(
                     data = listOf(0),
-                    originalPageOffset = -1,
-                    originalPageSize = 1,
-                    originalIndices = listOf(0)
+                    originalPageOffset = -1
                 )
             ),
             placeholdersBefore = 0,
@@ -75,22 +74,20 @@
             pages = listOf(
                 TransformablePage(
                     data = listOf(-1),
-                    originalPageOffset = -1,
-                    originalPageSize = 1,
-                    originalIndices = listOf(0)
+                    originalPageOffsets = intArrayOf(-1),
+                    hintOriginalPageOffset = -1,
+                    hintOriginalIndices = listOf(0)
                 ),
                 TransformablePage(
                     data = listOf(0),
-                    originalPageOffset = -1,
-                    originalPageSize = 1,
-                    originalIndices = listOf(0)
+                    originalPageOffset = -1
                 )
             ),
             placeholdersBefore = 0,
             combinedLoadStates = fullLoadStates
         )
 
-        assertEquals(expected, actual)
+        assertThat(actual).isEqualTo(expected)
     }
 
     @Test
@@ -99,9 +96,7 @@
             pages = listOf(
                 TransformablePage(
                     data = listOf("a"),
-                    originalPageOffset = 0,
-                    originalPageSize = 1,
-                    originalIndices = listOf(0)
+                    originalPageOffset = 0
                 )
             ),
             placeholdersBefore = 0,
@@ -113,21 +108,13 @@
             pages = listOf(
                 TransformablePage(
                     data = listOf("HEADER"),
-                    originalPageOffset = 0,
-                    originalPageSize = 1,
-                    originalIndices = listOf(0)
+                    originalPageOffsets = intArrayOf(0),
+                    hintOriginalPageOffset = 0,
+                    hintOriginalIndices = listOf(0)
                 ),
                 TransformablePage(
                     data = listOf("a"),
-                    originalPageOffset = 0,
-                    originalPageSize = 1,
-                    originalIndices = listOf(0)
-                ),
-                TransformablePage(
-                    data = listOf(),
-                    originalPageOffset = 0,
-                    originalPageSize = 1,
-                    originalIndices = null
+                    originalPageOffset = 0
                 )
             ),
             placeholdersBefore = 0,
@@ -135,7 +122,7 @@
             combinedLoadStates = fullLoadStates
         )
 
-        assertEquals(expected, actual)
+        assertThat(actual).isEqualTo(expected)
     }
 
     @Test
@@ -144,9 +131,7 @@
             pages = listOf(
                 TransformablePage(
                     data = emptyList<String>(),
-                    originalPageOffset = 0,
-                    originalPageSize = 0,
-                    originalIndices = emptyList()
+                    originalPageOffset = 0
                 )
             ),
             placeholdersBefore = 0,
@@ -158,9 +143,9 @@
             pages = listOf(
                 TransformablePage(
                     data = listOf("HEADER"),
-                    originalPageOffset = 0,
-                    originalPageSize = 0,
-                    originalIndices = listOf(0)
+                    originalPageOffsets = intArrayOf(0),
+                    hintOriginalPageOffset = 0,
+                    hintOriginalIndices = listOf(0)
                 )
             ),
             placeholdersBefore = 0,
@@ -177,9 +162,7 @@
             pages = listOf(
                 TransformablePage(
                     data = listOf("b"),
-                    originalPageOffset = 0,
-                    originalPageSize = 1,
-                    originalIndices = listOf(0)
+                    originalPageOffset = 0
                 )
             ),
             placeholdersAfter = 0,
@@ -190,15 +173,13 @@
             pages = listOf(
                 TransformablePage(
                     data = listOf("b"),
-                    originalPageOffset = 0,
-                    originalPageSize = 1,
-                    originalIndices = listOf(0)
+                    originalPageOffset = 0
                 ),
                 TransformablePage(
                     data = listOf("FOOTER"),
-                    originalPageOffset = 0,
-                    originalPageSize = 1,
-                    originalIndices = listOf(0)
+                    originalPageOffsets = intArrayOf(0),
+                    hintOriginalPageOffset = 0,
+                    hintOriginalIndices = listOf(0)
                 )
             ),
             placeholdersAfter = 0,
@@ -214,9 +195,7 @@
             pages = listOf(
                 TransformablePage(
                     data = listOf("a"),
-                    originalPageOffset = 0,
-                    originalPageSize = 1,
-                    originalIndices = listOf(0)
+                    originalPageOffset = 0
                 )
             ),
             placeholdersBefore = 0,
@@ -227,22 +206,14 @@
         val expected = Refresh(
             pages = listOf(
                 TransformablePage(
-                    data = listOf(),
-                    originalPageOffset = 0,
-                    originalPageSize = 1,
-                    originalIndices = null
-                ),
-                TransformablePage(
                     data = listOf("a"),
-                    originalPageOffset = 0,
-                    originalPageSize = 1,
-                    originalIndices = listOf(0)
+                    originalPageOffset = 0
                 ),
                 TransformablePage(
                     data = listOf("FOOTER"),
-                    originalPageOffset = 0,
-                    originalPageSize = 1,
-                    originalIndices = listOf(0)
+                    originalPageOffsets = intArrayOf(0),
+                    hintOriginalPageOffset = 0,
+                    hintOriginalIndices = listOf(0)
                 )
             ),
             placeholdersBefore = 0,
@@ -250,7 +221,7 @@
             combinedLoadStates = fullLoadStates
         )
 
-        assertEquals(expected, actual)
+        assertThat(actual).isEqualTo(expected)
     }
 
     @Test
@@ -259,9 +230,7 @@
             pages = listOf(
                 TransformablePage(
                     data = emptyList<String>(),
-                    originalPageOffset = 0,
-                    originalPageSize = 0,
-                    originalIndices = emptyList()
+                    originalPageOffset = 0
                 )
             ),
             placeholdersBefore = 0,
@@ -273,9 +242,9 @@
             pages = listOf(
                 TransformablePage(
                     data = listOf("FOOTER"),
-                    originalPageOffset = 0,
-                    originalPageSize = 0,
-                    originalIndices = listOf(0)
+                    originalPageOffsets = intArrayOf(0),
+                    hintOriginalPageOffset = 0,
+                    hintOriginalIndices = listOf(0)
                 )
             ),
             placeholdersBefore = 0,
@@ -283,6 +252,6 @@
             combinedLoadStates = fullLoadStates
         )
 
-        assertEquals(expected, actual)
+        assertThat(actual).isEqualTo(expected)
     }
 }
\ No newline at end of file
diff --git a/paging/common/src/test/kotlin/androidx/paging/PageEventTest.kt b/paging/common/src/test/kotlin/androidx/paging/PageEventTest.kt
index 5aec5b3..88d0583 100644
--- a/paging/common/src/test/kotlin/androidx/paging/PageEventTest.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/PageEventTest.kt
@@ -89,18 +89,20 @@
         assertFailsWith<IllegalArgumentException> {
             Drop<Char>(
                 loadType = REFRESH,
-                count = 2,
+                minPageOffset = 0,
+                maxPageOffset = 0,
                 placeholdersRemaining = 4
             )
         }
     }
 
     @Test
-    fun dropCount() {
+    fun dropRange() {
         assertFailsWith<IllegalArgumentException> {
             Drop<Char>(
                 loadType = REFRESH,
-                count = -1,
+                minPageOffset = 2,
+                maxPageOffset = 0,
                 placeholdersRemaining = 4
             )
         }
@@ -111,7 +113,8 @@
         assertFailsWith<IllegalArgumentException> {
             Drop<Char>(
                 loadType = REFRESH,
-                count = 1,
+                minPageOffset = 0,
+                maxPageOffset = 0,
                 placeholdersRemaining = -1
             )
         }
@@ -121,7 +124,8 @@
     fun dropTransform() = runBlockingTest {
         val drop = Drop<Char>(
             loadType = PREPEND,
-            count = 0,
+            minPageOffset = 0,
+            maxPageOffset = 0,
             placeholdersRemaining = 0
         )
 
@@ -171,10 +175,10 @@
             Append(
                 pages = listOf(
                     TransformablePage(
-                        originalPageOffset = 0,
+                        originalPageOffsets = intArrayOf(0),
                         data = listOf("a", "b"),
-                        originalPageSize = 4,
-                        originalIndices = listOf(0, 2)
+                        hintOriginalPageOffset = 0,
+                        hintOriginalIndices = listOf(0, 2)
                     )
                 ),
                 placeholdersAfter = 4,
@@ -183,10 +187,10 @@
             Append(
                 pages = listOf(
                     TransformablePage(
-                        originalPageOffset = 0,
+                        originalPageOffsets = intArrayOf(0),
                         data = listOf('a', 'b'),
-                        originalPageSize = 4,
-                        originalIndices = listOf(0, 2)
+                        hintOriginalPageOffset = 0,
+                        hintOriginalIndices = listOf(0, 2)
                     )
                 ),
                 placeholdersAfter = 4,
@@ -209,10 +213,10 @@
             Append(
                 pages = listOf(
                     TransformablePage(
-                        originalPageOffset = 0,
+                        originalPageOffsets = intArrayOf(0),
                         data = listOf('a', 'b', 'd'),
-                        originalPageSize = 4,
-                        originalIndices = listOf(0, 1, 3)
+                        hintOriginalPageOffset = 0,
+                        hintOriginalIndices = listOf(0, 1, 3)
                     )
                 ),
                 placeholdersAfter = 4,
@@ -226,10 +230,10 @@
             Append(
                 pages = listOf(
                     TransformablePage(
-                        originalPageOffset = 0,
+                        originalPageOffsets = intArrayOf(0),
                         data = listOf('b', 'd'),
-                        originalPageSize = 4,
-                        originalIndices = listOf(1, 3)
+                        hintOriginalPageOffset = 0,
+                        hintOriginalIndices = listOf(1, 3)
                     )
                 ),
                 placeholdersAfter = 4,
@@ -255,10 +259,10 @@
             Append(
                 pages = listOf(
                     TransformablePage(
-                        originalPageOffset = 0,
+                        originalPageOffsets = intArrayOf(0),
                         data = listOf("a1", "a2", "b1", "b2"),
-                        originalPageSize = 2,
-                        originalIndices = listOf(0, 0, 1, 1)
+                        hintOriginalPageOffset = 0,
+                        hintOriginalIndices = listOf(0, 0, 1, 1)
                     )
                 ),
                 placeholdersAfter = 4,
@@ -275,10 +279,10 @@
             Append(
                 pages = listOf(
                     TransformablePage(
-                        originalPageOffset = 0,
+                        originalPageOffsets = intArrayOf(0),
                         data = listOf("a1", "-", "a2", "-", "b1", "-", "b2", "-"),
-                        originalPageSize = 2,
-                        originalIndices = listOf(0, 0, 0, 0, 1, 1, 1, 1)
+                        hintOriginalPageOffset = 0,
+                        hintOriginalIndices = listOf(0, 0, 0, 0, 1, 1, 1, 1)
                     )
                 ),
                 placeholdersAfter = 4,
diff --git a/paging/common/src/test/kotlin/androidx/paging/PageFetcherSnapshotStateTest.kt b/paging/common/src/test/kotlin/androidx/paging/PageFetcherSnapshotStateTest.kt
index e0f12f5..30ee3f8 100644
--- a/paging/common/src/test/kotlin/androidx/paging/PageFetcherSnapshotStateTest.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/PageFetcherSnapshotStateTest.kt
@@ -99,8 +99,22 @@
         assertEquals(0, pagerState.placeholdersBefore)
         assertEquals(0, pagerState.placeholdersAfter)
 
-        pagerState.drop(loadType = PREPEND, pageCount = 1, placeholdersRemaining = 100)
-        pagerState.drop(loadType = APPEND, pageCount = 1, placeholdersRemaining = 100)
+        pagerState.drop(
+            event = PageEvent.Drop(
+                loadType = PREPEND,
+                minPageOffset = -2,
+                maxPageOffset = -2,
+                placeholdersRemaining = 100
+            )
+        )
+        pagerState.drop(
+            event = PageEvent.Drop(
+                loadType = APPEND,
+                minPageOffset = 2,
+                maxPageOffset = 2,
+                placeholdersRemaining = 100
+            )
+        )
 
         assertEquals(0, pagerState.placeholdersBefore)
         assertEquals(0, pagerState.placeholdersAfter)
@@ -184,8 +198,22 @@
         assertEquals(24, pagerState.placeholdersBefore)
         assertEquals(24, pagerState.placeholdersAfter)
 
-        pagerState.drop(loadType = PREPEND, pageCount = 1, placeholdersRemaining = 100)
-        pagerState.drop(loadType = APPEND, pageCount = 1, placeholdersRemaining = 100)
+        pagerState.drop(
+            event = PageEvent.Drop(
+                loadType = PREPEND,
+                minPageOffset = -2,
+                maxPageOffset = -2,
+                placeholdersRemaining = 100
+            )
+        )
+        pagerState.drop(
+            event = PageEvent.Drop(
+                loadType = APPEND,
+                minPageOffset = 2,
+                maxPageOffset = 2,
+                placeholdersRemaining = 100
+            )
+        )
 
         assertEquals(100, pagerState.placeholdersBefore)
         assertEquals(100, pagerState.placeholdersAfter)
diff --git a/paging/common/src/test/kotlin/androidx/paging/PageFetcherSnapshotTest.kt b/paging/common/src/test/kotlin/androidx/paging/PageFetcherSnapshotTest.kt
index 950424a..ccd230c 100644
--- a/paging/common/src/test/kotlin/androidx/paging/PageFetcherSnapshotTest.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/PageFetcherSnapshotTest.kt
@@ -157,7 +157,12 @@
         assertThat(fetcherState.newEvents()).isEqualTo(
             listOf<PageEvent<Int>>(
                 LoadStateUpdate(APPEND, false, Loading),
-                Drop(loadType = PREPEND, count = 1, placeholdersRemaining = 1),
+                Drop(
+                    loadType = PREPEND,
+                    minPageOffset = -1,
+                    maxPageOffset = -1,
+                    placeholdersRemaining = 1
+                ),
                 createAppend(
                     pageOffset = 1,
                     range = 3..3,
@@ -239,7 +244,12 @@
                 endState = NotLoading.Complete
             ),
             LoadStateUpdate(PREPEND, false, Loading),
-            Drop(APPEND, 1, 1),
+            Drop(
+                loadType = APPEND,
+                minPageOffset = 1,
+                maxPageOffset = 1,
+                placeholdersRemaining = 1
+            ),
             createPrepend(
                 pageOffset = -1,
                 range = 96..96,
@@ -408,7 +418,12 @@
                 LoadStateUpdate(PREPEND, false, Loading),
                 createPrepend(pageOffset = -1, range = 48..49),
                 LoadStateUpdate(PREPEND, false, Loading),
-                Drop(APPEND, 1, 50),
+                Drop(
+                    loadType = APPEND,
+                    minPageOffset = 0,
+                    maxPageOffset = 0,
+                    placeholdersRemaining = 50
+                ),
                 createPrepend(pageOffset = -2, range = 46..47)
             )
 
@@ -539,7 +554,12 @@
                 listOf<PageEvent<Int>>(
                     LoadStateUpdate(PREPEND, false, Loading),
                     LoadStateUpdate(APPEND, false, Loading),
-                    Drop(APPEND, 1, 50),
+                    Drop(
+                        loadType = APPEND,
+                        minPageOffset = 0,
+                        maxPageOffset = 0,
+                        placeholdersRemaining = 50
+                    ),
                     createPrepend(pageOffset = -2, range = 46..47)
                 )
             )
@@ -640,9 +660,19 @@
         assertThat(fetcherState.newEvents()).isEqualTo(
             listOf<PageEvent<Int>>(
                 LoadStateUpdate(loadType = APPEND, fromMediator = false, loadState = Loading),
-                Drop(loadType = PREPEND, count = 1, placeholdersRemaining = 49),
+                Drop(
+                    loadType = PREPEND,
+                    minPageOffset = -2,
+                    maxPageOffset = -2,
+                    placeholdersRemaining = 49
+                ),
                 createAppend(pageOffset = 1, range = 53..53, endState = Loading),
-                Drop(loadType = PREPEND, count = 1, placeholdersRemaining = 50),
+                Drop(
+                    loadType = PREPEND,
+                    minPageOffset = -1,
+                    maxPageOffset = -1,
+                    placeholdersRemaining = 50
+                ),
                 createAppend(pageOffset = 2, range = 54..54)
             )
         )
@@ -662,7 +692,12 @@
         assertThat(fetcherState.newEvents()).isEqualTo(
             listOf<PageEvent<Int>>(
                 LoadStateUpdate(loadType = PREPEND, fromMediator = false, loadState = Loading),
-                Drop(loadType = APPEND, count = 1, placeholdersRemaining = 46),
+                Drop(
+                    loadType = APPEND,
+                    minPageOffset = 2,
+                    maxPageOffset = 2,
+                    placeholdersRemaining = 46
+                ),
                 createPrepend(pageOffset = -1, range = 49..49)
             )
         )
@@ -793,7 +828,12 @@
         assertThat(fetcherState.newEvents()).isEqualTo(
             listOf<PageEvent<Int>>(
                 LoadStateUpdate(APPEND, false, Loading),
-                Drop(PREPEND, 1, 52),
+                Drop(
+                    loadType = PREPEND,
+                    minPageOffset = 0,
+                    maxPageOffset = 0,
+                    placeholdersRemaining = 52
+                ),
                 createAppend(pageOffset = 2, range = 54..55)
             )
         )
@@ -842,7 +882,7 @@
                 createAppend(pageOffset = 2, range = 56..56)
             )
 
-            assertEvents(expected, fetcherState.pageEventLists[0])
+            assertThat(fetcherState.pageEventLists[0]).isEqualTo(expected)
             fetcherState.job.cancel()
         }
     }
@@ -913,7 +953,12 @@
                 listOf<PageEvent<Int>>(
                     LoadStateUpdate(APPEND, false, Loading),
                     LoadStateUpdate(PREPEND, false, Loading),
-                    Drop(PREPEND, 1, 52),
+                    Drop(
+                        loadType = PREPEND,
+                        minPageOffset = 0,
+                        maxPageOffset = 0,
+                        placeholdersRemaining = 52
+                    ),
                     createAppend(
                         pageOffset = 2,
                         range = 54..55,
@@ -982,9 +1027,19 @@
         assertThat(fetcherState.newEvents()).isEqualTo(
             listOf<PageEvent<Int>>(
                 LoadStateUpdate(loadType = PREPEND, fromMediator = false, loadState = Loading),
-                Drop(loadType = APPEND, count = 1, placeholdersRemaining = 46),
+                Drop(
+                    loadType = APPEND,
+                    minPageOffset = 2,
+                    maxPageOffset = 2,
+                    placeholdersRemaining = 46
+                ),
                 createPrepend(pageOffset = -1, range = 49..49, startState = Loading),
-                Drop(loadType = APPEND, count = 1, placeholdersRemaining = 47),
+                Drop(
+                    loadType = APPEND,
+                    minPageOffset = 1,
+                    maxPageOffset = 1,
+                    placeholdersRemaining = 47
+                ),
                 createPrepend(pageOffset = -2, range = 48..48)
             )
         )
@@ -1004,7 +1059,12 @@
         assertThat(fetcherState.newEvents()).isEqualTo(
             listOf<PageEvent<Int>>(
                 LoadStateUpdate(loadType = APPEND, fromMediator = false, loadState = Loading),
-                Drop(loadType = PREPEND, count = 1, placeholdersRemaining = 49),
+                Drop(
+                    loadType = PREPEND,
+                    minPageOffset = -2,
+                    maxPageOffset = -2,
+                    placeholdersRemaining = 49
+                ),
                 createAppend(pageOffset = 1, range = 53..53)
             )
         )
@@ -2058,9 +2118,7 @@
                         pages = listOf(
                             TransformablePage(
                                 originalPageOffset = 0,
-                                data = listOf(0),
-                                originalPageSize = 1,
-                                originalIndices = null
+                                data = listOf(0)
                             )
                         ),
                         placeholdersBefore = 0,
@@ -2076,9 +2134,7 @@
                         pages = listOf(
                             TransformablePage(
                                 originalPageOffset = -1,
-                                data = listOf(),
-                                originalPageSize = 0,
-                                originalIndices = null
+                                data = listOf()
                             )
                         ),
                         placeholdersBefore = 0,
@@ -2132,9 +2188,7 @@
                         pages = listOf(
                             TransformablePage(
                                 originalPageOffset = 0,
-                                data = listOf(0),
-                                originalPageSize = 1,
-                                originalIndices = null
+                                data = listOf(0)
                             )
                         ),
                         placeholdersBefore = 0,
@@ -2150,9 +2204,7 @@
                         pages = listOf(
                             TransformablePage(
                                 originalPageOffset = -1,
-                                data = listOf(),
-                                originalPageSize = 0,
-                                originalIndices = null
+                                data = listOf()
                             )
                         ),
                         placeholdersBefore = 0,
@@ -2221,9 +2273,7 @@
                             pages = listOf(
                                 TransformablePage(
                                     originalPageOffset = 0,
-                                    data = listOf(1, 2, 3),
-                                    originalPageSize = 3,
-                                    originalIndices = null
+                                    data = listOf(1, 2, 3)
                                 )
                             ),
                             placeholdersBefore = 1,
@@ -2239,9 +2289,7 @@
                             pages = listOf(
                                 TransformablePage(
                                     originalPageOffset = -1,
-                                    data = listOf(0),
-                                    originalPageSize = 1,
-                                    originalIndices = null
+                                    data = listOf(0)
                                 )
                             ),
                             placeholdersBefore = 0,
@@ -2258,9 +2306,7 @@
                             pages = listOf(
                                 TransformablePage(
                                     originalPageOffset = -2,
-                                    data = listOf(),
-                                    originalPageSize = 0,
-                                    originalIndices = null
+                                    data = listOf()
                                 )
                             ),
                             placeholdersBefore = 0,
@@ -2316,9 +2362,7 @@
                         pages = listOf(
                             TransformablePage(
                                 originalPageOffset = 0,
-                                data = listOf(99),
-                                originalPageSize = 1,
-                                originalIndices = null
+                                data = listOf(99)
                             )
                         ),
                         placeholdersBefore = 99,
@@ -2334,9 +2378,7 @@
                         pages = listOf(
                             TransformablePage(
                                 originalPageOffset = 1,
-                                data = listOf(),
-                                originalPageSize = 0,
-                                originalIndices = null
+                                data = listOf()
                             )
                         ),
                         placeholdersAfter = 0,
@@ -2390,9 +2432,7 @@
                         pages = listOf(
                             TransformablePage(
                                 originalPageOffset = 0,
-                                data = listOf(99),
-                                originalPageSize = 1,
-                                originalIndices = null
+                                data = listOf(99)
                             )
                         ),
                         placeholdersBefore = 99,
@@ -2408,9 +2448,7 @@
                         pages = listOf(
                             TransformablePage(
                                 originalPageOffset = 1,
-                                data = listOf(),
-                                originalPageSize = 0,
-                                originalIndices = null
+                                data = listOf()
                             )
                         ),
                         placeholdersAfter = 0,
@@ -2479,9 +2517,7 @@
                             pages = listOf(
                                 TransformablePage(
                                     originalPageOffset = 0,
-                                    data = listOf(96, 97, 98),
-                                    originalPageSize = 3,
-                                    originalIndices = null
+                                    data = listOf(96, 97, 98)
                                 )
                             ),
                             placeholdersBefore = 96,
@@ -2497,9 +2533,7 @@
                             pages = listOf(
                                 TransformablePage(
                                     originalPageOffset = 1,
-                                    data = listOf(99),
-                                    originalPageSize = 1,
-                                    originalIndices = null
+                                    data = listOf(99)
                                 )
                             ),
                             placeholdersAfter = 0,
@@ -2516,9 +2550,7 @@
                             pages = listOf(
                                 TransformablePage(
                                     originalPageOffset = 2,
-                                    data = listOf(),
-                                    originalPageSize = 0,
-                                    originalIndices = null
+                                    data = listOf()
                                 )
                             ),
                             placeholdersAfter = 0,
@@ -2586,9 +2618,7 @@
                             pages = listOf(
                                 TransformablePage(
                                     originalPageOffset = 0,
-                                    data = listOf(50),
-                                    originalPageSize = 1,
-                                    originalIndices = null
+                                    data = listOf(50)
                                 )
                             ),
                             placeholdersBefore = 50,
diff --git a/paging/common/src/test/kotlin/androidx/paging/PagePresenterTest.kt b/paging/common/src/test/kotlin/androidx/paging/PagePresenterTest.kt
index 9da7abe..f383d13 100644
--- a/paging/common/src/test/kotlin/androidx/paging/PagePresenterTest.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/PagePresenterTest.kt
@@ -20,8 +20,10 @@
 import androidx.paging.LoadType.APPEND
 import androidx.paging.LoadType.PREPEND
 import androidx.paging.LoadType.REFRESH
+import androidx.paging.PageEvent.Insert.Companion.Refresh
 import androidx.paging.PagePresenter.ProcessPageEventCallback
 import androidx.paging.PagingSource.LoadResult.Page.Companion.COUNT_UNDEFINED
+import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
@@ -36,13 +38,11 @@
     trailingNullCount: Int = COUNT_UNDEFINED,
     indexOfInitialPage: Int = 0
 ) = PagePresenter(
-    PageEvent.Insert.Refresh(
+    Refresh(
         pages = pages.mapIndexed { index, list ->
             TransformablePage(
                 originalPageOffset = index - indexOfInitialPage,
-                data = list,
-                originalPageSize = list.size,
-                originalIndices = null
+                data = list
             )
         },
         placeholdersBefore = leadingNullCount,
@@ -68,13 +68,15 @@
 
 internal fun <T : Any> PagePresenter<T>.dropPages(
     isPrepend: Boolean,
-    pagesToDrop: Int,
+    minPageOffset: Int,
+    maxPageOffset: Int,
     placeholdersRemaining: Int,
     callback: ProcessPageEventCallback
 ) = processEvent(
     PageEvent.Drop(
         loadType = if (isPrepend) PREPEND else APPEND,
-        count = pagesToDrop,
+        minPageOffset = minPageOffset,
+        maxPageOffset = maxPageOffset,
         placeholdersRemaining = placeholdersRemaining
     ),
     callback
@@ -309,11 +311,16 @@
         assertEquals(initialPages.flatten() + List<Char?>(initialNulls) { null }, data.asList())
 
         val callback = ProcessPageEventCallbackCapture()
-        data.dropPages(false, pagesToDrop, newNulls, callback)
+        data.dropPages(
+            isPrepend = false,
+            minPageOffset = initialPages.lastIndex - (pagesToDrop - 1),
+            maxPageOffset = initialPages.lastIndex,
+            placeholdersRemaining = newNulls,
+            callback = callback
+        )
 
-        assertEquals(
-            events + listOf(StateEvent(APPEND, false, NotLoading.Incomplete)),
-            callback.getAllAndClear()
+        assertThat(callback.getAllAndClear()).isEqualTo(
+            events + listOf(StateEvent(APPEND, false, NotLoading.Incomplete))
         )
 
         // assert final list state
@@ -345,11 +352,16 @@
         )
 
         val callback = ProcessPageEventCallbackCapture()
-        data.dropPages(true, pagesToDrop, newNulls, callback)
+        data.dropPages(
+            isPrepend = true,
+            minPageOffset = 0,
+            maxPageOffset = pagesToDrop - 1,
+            placeholdersRemaining = newNulls,
+            callback = callback
+        )
 
-        assertEvents(
-            events + listOf(StateEvent(PREPEND, false, NotLoading.Incomplete)),
-            callback.getAllAndClear()
+        assertThat(callback.getAllAndClear()).isEqualTo(
+            events + listOf(StateEvent(PREPEND, false, NotLoading.Incomplete))
         )
 
         // assert final list state
@@ -370,19 +382,6 @@
     }
 
     @Test
-    fun dropPageNoop() = verifyDrop(
-        initialPages = listOf(
-            listOf('a', 'b'),
-            listOf('c', 'd')
-        ),
-        initialNulls = 0,
-        newNulls = 0,
-        pagesToDrop = 0,
-        startEvents = emptyList(),
-        endEvents = emptyList()
-    )
-
-    @Test
     fun dropPageMulti() = verifyDrop(
         initialPages = listOf(
             listOf('a', 'b'),
@@ -420,8 +419,14 @@
         initialNulls = 0,
         newNulls = 3,
         pagesToDrop = 2,
-        startEvents = listOf(ChangeEvent(0, 3)),
-        endEvents = listOf(ChangeEvent(2, 3))
+        startEvents = listOf(
+            // [null, null, null, 'a', 'b']
+            ChangeEvent(0, 3)
+        ),
+        endEvents = listOf(
+            // ['a', 'b', null, null, null]
+            ChangeEvent(2, 3)
+        )
     )
 
     @Test
@@ -435,12 +440,16 @@
         newNulls = 4,
         pagesToDrop = 2,
         startEvents = listOf(
-            ChangeEvent(2, 3),
-            RemoveEvent(0, 1)
+            // [null, 'e', 'c', 'd', 'a', 'b']
+            RemoveEvent(0, 1),
+            // [null, null, null, null, 'a', 'b']
+            ChangeEvent(1, 3)
         ),
         endEvents = listOf(
-            ChangeEvent(2, 3),
-            RemoveEvent(6, 1)
+            // ['a', 'b', 'c', 'd', 'e', null]
+            RemoveEvent(6, 1),
+            // ['a', 'b', null, null, null, null]
+            ChangeEvent(2, 3)
         )
     )
 
@@ -455,12 +464,16 @@
         newNulls = 1,
         pagesToDrop = 2,
         startEvents = listOf(
-            ChangeEvent(2, 1),
-            RemoveEvent(0, 2)
+            // ['d', 'a', 'b']
+            RemoveEvent(0, 2),
+            // [null, 'a', 'b']
+            ChangeEvent(0, 1)
         ),
         endEvents = listOf(
-            ChangeEvent(2, 1),
-            RemoveEvent(3, 2)
+            // ['a', 'b', 'c']
+            RemoveEvent(3, 2),
+            // ['a', 'b', null]
+            ChangeEvent(2, 1)
         )
     )
 
@@ -475,14 +488,16 @@
         newNulls = 1,
         pagesToDrop = 2,
         startEvents = listOf(
-            ChangeEvent(5, 1),
-            RemoveEvent(0, 2),
-            RemoveEvent(0, 3)
+            // ['d', 'a', 'b']
+            RemoveEvent(0, 5),
+            // [null, 'a', 'b']
+            ChangeEvent(0, 1)
         ),
         endEvents = listOf(
-            ChangeEvent(2, 1),
-            RemoveEvent(3, 2),
-            RemoveEvent(3, 3)
+            // ['a', 'b', 'c']
+            RemoveEvent(3, 5),
+            // ['a', 'b', null]
+            ChangeEvent(2, 1)
         )
     )
 
@@ -507,7 +522,7 @@
     @Test
     fun snapshot_uncounted() {
         val pagePresenter = PagePresenter(
-            PageEvent.Insert.Refresh(
+            insertEvent = Refresh(
                 pages = listOf(TransformablePage(listOf('a'))),
                 placeholdersBefore = 0,
                 placeholdersAfter = 0,
@@ -521,7 +536,7 @@
     @Test
     fun snapshot_counted() {
         val pagePresenter = PagePresenter(
-            PageEvent.Insert.Refresh(
+            insertEvent = Refresh(
                 pages = listOf(TransformablePage(listOf('a'))),
                 placeholdersBefore = 1,
                 placeholdersAfter = 3,
diff --git a/paging/common/src/test/kotlin/androidx/paging/PagingDataDifferTest.kt b/paging/common/src/test/kotlin/androidx/paging/PagingDataDifferTest.kt
index e5d1060..cfed561 100644
--- a/paging/common/src/test/kotlin/androidx/paging/PagingDataDifferTest.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/PagingDataDifferTest.kt
@@ -16,8 +16,8 @@
 
 package androidx.paging
 
-import androidx.paging.LoadType.PREPEND
 import androidx.paging.LoadState.NotLoading
+import androidx.paging.LoadType.PREPEND
 import androidx.paging.PageEvent.Drop
 import androidx.paging.PageEvent.Insert.Companion.Append
 import androidx.paging.PageEvent.Insert.Companion.Prepend
@@ -152,7 +152,7 @@
         val pageEventFlow = flowOf<PageEvent<Int>>(
             Refresh(listOf(), 0, 0, CombinedLoadStates.IDLE_SOURCE),
             Prepend(listOf(), 0, CombinedLoadStates.IDLE_SOURCE),
-            Drop(PREPEND, 0, 0),
+            Drop(PREPEND, -1, -1, 0),
             Refresh(listOf(TransformablePage(0, listOf(0))), 0, 0, CombinedLoadStates.IDLE_SOURCE)
         )
 
@@ -183,7 +183,7 @@
         val pageEventFlow = flowOf<PageEvent<Int>>(
             Refresh(listOf(), 0, 0, CombinedLoadStates.IDLE_SOURCE),
             Prepend(listOf(), 0, CombinedLoadStates.IDLE_SOURCE),
-            Drop(PREPEND, 0, 0),
+            Drop(PREPEND, -1, -1, 0),
             Refresh(listOf(TransformablePage(0, listOf(0))), 0, 0, CombinedLoadStates.IDLE_SOURCE)
         )
 
@@ -205,7 +205,7 @@
         job.cancel()
     }
 
-        @Test
+    @Test
     fun fetch_loadHintResentWhenUnfulfilled() = testScope.runBlockingTest {
         val differ = SimpleDiffer(dummyDifferCallback)
 
@@ -432,7 +432,14 @@
 
         // Drop the previous page, which reset resendable index state in the PREPEND direction.
         // [null, null, [-1], [1], [3], null, null]
-        pageEventCh.offer(Drop(loadType = PREPEND, count = 1, placeholdersRemaining = 2))
+        pageEventCh.offer(
+            Drop(
+                loadType = PREPEND,
+                minPageOffset = -2,
+                maxPageOffset = -2,
+                placeholdersRemaining = 2
+            )
+        )
 
         // Re-insert the previous page, which should not trigger resending the index due to
         // previous page drop:
diff --git a/paging/common/src/test/kotlin/androidx/paging/SeparatorsTest.kt b/paging/common/src/test/kotlin/androidx/paging/SeparatorsTest.kt
index 8d8bf2b..4edaec7 100644
--- a/paging/common/src/test/kotlin/androidx/paging/SeparatorsTest.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/SeparatorsTest.kt
@@ -23,6 +23,7 @@
 import androidx.paging.PageEvent.Insert.Companion.Append
 import androidx.paging.PageEvent.Insert.Companion.Prepend
 import androidx.paging.PageEvent.Insert.Companion.Refresh
+import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.toList
@@ -32,21 +33,6 @@
 import org.junit.runners.JUnit4
 import kotlin.test.assertEquals
 
-private fun <T : Any> assertEvents(expected: List<PageEvent<T>>, actual: List<PageEvent<T>>) {
-    try {
-        assertEquals(expected, actual)
-    } catch (e: Throwable) {
-        throw AssertionError(
-            e.localizedMessage
-                .replace("),", "),\n")
-                .replace("<[", "<[\n ")
-                .replace("actual", "\nactual\n")
-                .replace("Expected", "\nExpected\n")
-                .replace("pages=", "pages=\n")
-        )
-    }
-}
-
 private fun <T : Any> List<PageEvent<T>>.getItems() = mapNotNull { event ->
     when (event) {
         is PageEvent.Insert<T> -> event.pages.map { it.data }
@@ -73,34 +59,7 @@
 class SeparatorsTest {
     @Test
     fun refreshFull() = runBlockingTest {
-        assertEvents(
-            listOf(
-                Refresh(
-                    pages = listOf(
-                        TransformablePage(
-                            originalPageOffset = 0,
-                            data = listOf("a2", "B", "b1"),
-                            originalPageSize = 2,
-                            originalIndices = listOf(0, 1, 1)
-                        ),
-                        TransformablePage(
-                            originalPageOffset = 1,
-                            data = listOf("C"),
-                            originalPageSize = 2,
-                            originalIndices = listOf(0)
-                        ),
-                        TransformablePage(
-                            originalPageOffset = 1,
-                            data = listOf("c1", "c2"),
-                            originalPageSize = 2,
-                            originalIndices = null
-                        )
-                    ),
-                    placeholdersBefore = 0,
-                    placeholdersAfter = 1,
-                    combinedLoadStates = localLoadStatesOf()
-                )
-            ),
+        assertThat(
             flowOf(
                 Refresh(
                     pages = listOf(
@@ -112,6 +71,32 @@
                     combinedLoadStates = localLoadStatesOf()
                 )
             ).insertEventSeparators(LETTER_SEPARATOR_GENERATOR).toList()
+        ).isEqualTo(
+            listOf(
+                Refresh(
+                    pages = listOf(
+                        TransformablePage(
+                            originalPageOffsets = intArrayOf(0),
+                            data = listOf("a2", "B", "b1"),
+                            hintOriginalPageOffset = 0,
+                            hintOriginalIndices = listOf(0, 1, 1)
+                        ),
+                        TransformablePage(
+                            originalPageOffsets = intArrayOf(0, 1),
+                            data = listOf("C"),
+                            hintOriginalPageOffset = 1,
+                            hintOriginalIndices = listOf(0)
+                        ),
+                        TransformablePage(
+                            originalPageOffset = 1,
+                            data = listOf("c1", "c2")
+                        )
+                    ),
+                    placeholdersBefore = 0,
+                    placeholdersAfter = 1,
+                    combinedLoadStates = localLoadStatesOf()
+                )
+            )
         )
     }
 
@@ -125,40 +110,8 @@
             placeholdersAfter = 1,
             combinedLoadStates = localLoadStatesOf()
         )
-        assertEvents(
-            listOf(
-                refresh,
-                Prepend(
-                    pages = listOf(
-                        TransformablePage(
-                            originalPageOffset = -2,
-                            data = listOf("a1", "B", "b1"),
-                            originalPageSize = 2,
-                            originalIndices = listOf(0, 1, 1)
-                        ),
-                        TransformablePage(
-                            originalPageOffset = -1,
-                            data = listOf(),
-                            originalPageSize = 2,
-                            originalIndices = null
-                        ),
-                        TransformablePage(
-                            originalPageOffset = -1,
-                            data = listOf("b2", "b3"),
-                            originalPageSize = 2,
-                            originalIndices = null
-                        ),
-                        TransformablePage(
-                            originalPageOffset = -1,
-                            data = listOf("C"),
-                            originalPageSize = 2,
-                            originalIndices = listOf(1) // note: using last index of 2nd page in
-                        )
-                    ),
-                    placeholdersBefore = 1,
-                    combinedLoadStates = localLoadStatesOf()
-                )
-            ),
+
+        assertThat(
             flowOf(
                 refresh,
                 Prepend(
@@ -170,6 +123,32 @@
                     combinedLoadStates = localLoadStatesOf()
                 )
             ).insertEventSeparators(LETTER_SEPARATOR_GENERATOR).toList()
+        ).isEqualTo(
+            listOf(
+                refresh,
+                Prepend(
+                    pages = listOf(
+                        TransformablePage(
+                            originalPageOffsets = intArrayOf(-2),
+                            data = listOf("a1", "B", "b1"),
+                            hintOriginalPageOffset = -2,
+                            hintOriginalIndices = listOf(0, 1, 1)
+                        ),
+                        TransformablePage(
+                            originalPageOffset = -1,
+                            data = listOf("b2", "b3")
+                        ),
+                        TransformablePage(
+                            originalPageOffsets = intArrayOf(-1, 0),
+                            data = listOf("C"),
+                            hintOriginalPageOffset = -1,
+                            hintOriginalIndices = listOf(1)
+                        )
+                    ),
+                    placeholdersBefore = 1,
+                    combinedLoadStates = localLoadStatesOf()
+                )
+            )
         )
     }
 
@@ -183,40 +162,7 @@
             placeholdersAfter = 1,
             combinedLoadStates = localLoadStatesOf()
         )
-        assertEvents(
-            listOf(
-                refresh,
-                Append(
-                    pages = listOf(
-                        TransformablePage(
-                            originalPageOffset = 1,
-                            data = listOf("C"),
-                            originalPageSize = 2,
-                            originalIndices = listOf(0)
-                        ),
-                        TransformablePage(
-                            originalPageOffset = 1,
-                            data = listOf("c1", "D", "d1"),
-                            originalPageSize = 2,
-                            originalIndices = listOf(0, 1, 1)
-                        ),
-                        TransformablePage(
-                            originalPageOffset = 2,
-                            data = listOf(),
-                            originalPageSize = 2,
-                            originalIndices = null
-                        ),
-                        TransformablePage(
-                            originalPageOffset = 2,
-                            data = listOf("d2", "d3"),
-                            originalPageSize = 2,
-                            originalIndices = null
-                        )
-                    ),
-                    placeholdersAfter = 1,
-                    combinedLoadStates = localLoadStatesOf()
-                )
-            ),
+        assertThat(
             flowOf(
                 refresh,
                 Append(
@@ -228,41 +174,39 @@
                     combinedLoadStates = localLoadStatesOf()
                 )
             ).insertEventSeparators(LETTER_SEPARATOR_GENERATOR).toList()
+        ).isEqualTo(
+            listOf(
+                refresh,
+                Append(
+                    pages = listOf(
+                        TransformablePage(
+                            originalPageOffsets = intArrayOf(0, 1),
+                            data = listOf("C"),
+                            hintOriginalPageOffset = 1,
+                            hintOriginalIndices = listOf(0)
+                        ),
+                        TransformablePage(
+                            originalPageOffsets = intArrayOf(1),
+                            data = listOf("c1", "D", "d1"),
+                            hintOriginalPageOffset = 1,
+                            hintOriginalIndices = listOf(0, 1, 1)
+                        ),
+                        TransformablePage(
+                            originalPageOffset = 2,
+                            data = listOf("d2", "d3")
+                        )
+                    ),
+                    placeholdersAfter = 1,
+                    combinedLoadStates = localLoadStatesOf()
+                )
+            )
         )
     }
 
     @Test
     fun refreshDropFull() = runBlockingTest {
-        assertEvents(
-            expected = listOf(
-                Refresh(
-                    pages = listOf(
-                        TransformablePage(
-                            originalPageOffset = 0,
-                            data = listOf("a1"),
-                            originalPageSize = 1,
-                            originalIndices = null
-                        ),
-                        TransformablePage(
-                            originalPageOffset = 1,
-                            data = listOf(),
-                            originalPageSize = 1,
-                            originalIndices = null
-                        ),
-                        TransformablePage(
-                            originalPageOffset = 1,
-                            data = listOf("a2"),
-                            originalPageSize = 1,
-                            originalIndices = null
-                        )
-                    ),
-                    placeholdersBefore = 0,
-                    placeholdersAfter = 1,
-                    combinedLoadStates = localLoadStatesOf()
-                ),
-                Drop<String>(APPEND, 2, 4)
-            ),
-            actual = flowOf(
+        assertThat(
+            flowOf(
                 Refresh(
                     pages = listOf(
                         listOf("a1"),
@@ -272,8 +216,27 @@
                     placeholdersAfter = 1,
                     combinedLoadStates = localLoadStatesOf()
                 ),
-                Drop<String>(APPEND, 1, 4)
+                Drop<String>(APPEND, 1, 1, 4)
             ).insertEventSeparators(LETTER_SEPARATOR_GENERATOR).toList()
+        ).isEqualTo(
+            listOf(
+                Refresh(
+                    pages = listOf(
+                        TransformablePage(
+                            originalPageOffset = 0,
+                            data = listOf("a1")
+                        ),
+                        TransformablePage(
+                            originalPageOffset = 1,
+                            data = listOf("a2")
+                        )
+                    ),
+                    placeholdersBefore = 0,
+                    placeholdersAfter = 1,
+                    combinedLoadStates = localLoadStatesOf()
+                ),
+                Drop<String>(APPEND, 1, 1, 4)
+            )
         )
     }
 
@@ -312,10 +275,12 @@
         combinedLoadStates = localLoadStatesOf(appendLocal = append)
     )
 
-    private fun drop(
-        loadType: LoadType,
-        count: Int
-    ) = Drop<String>(loadType = loadType, count = count, placeholdersRemaining = 0)
+    private fun drop(loadType: LoadType, minPageOffset: Int, maxPageOffset: Int) = Drop<String>(
+        loadType = loadType,
+        minPageOffset = minPageOffset,
+        maxPageOffset = maxPageOffset,
+        placeholdersRemaining = 0
+    )
 
     @Test
     fun refreshNoop() = runBlockingTest {
@@ -433,14 +398,14 @@
             listOf(
                 // not enough data to create separators yet
                 refresh(pages = listOf()),
-                // don't insert a separator, since a drop occurred (even though it's 0 size)
+                // don't insert a separator, since a drop occurred
                 append(pages = listOf("a1")),
                 // but now add the separator, since start is done again
                 prepend(pages = listOf("A"))
             ),
             flowOf(
                 refresh(pages = listOf(), prepend = NotLoading.Complete),
-                drop(loadType = PREPEND, count = 0),
+                drop(loadType = PREPEND, minPageOffset = 0, maxPageOffset = 0),
                 append(pages = listOf("a1")),
                 prepend(pages = listOf(), prepend = NotLoading.Complete)
             ).insertEventSeparators(LETTER_SEPARATOR_GENERATOR).toList()
@@ -453,14 +418,14 @@
             listOf(
                 // not enough data to create separators yet
                 refresh(pages = listOf()),
-                // don't insert a separator, since a drop occurred (even though it's 0 size)
+                // don't insert a separator, since a drop occurred
                 prepend(pages = listOf("a1")),
                 // but now add the separator, since end is done again
                 append(pages = listOf("END"))
             ),
             flowOf(
                 refresh(pages = listOf(), append = NotLoading.Complete),
-                drop(loadType = APPEND, count = 0),
+                drop(loadType = APPEND, minPageOffset = 0, maxPageOffset = 0),
                 prepend(pages = listOf("a1")),
                 append(pages = listOf(), append = NotLoading.Complete)
             ).insertEventSeparators(LETTER_SEPARATOR_GENERATOR).toList()
@@ -500,96 +465,90 @@
     @Test
     fun refreshEmptyStartDropFull() = runBlockingTest {
         // when start terminal separator is inserted, we need to drop count*2 + 1
-        assertEvents(
+        assertThat(
+            flowOf(
+                refresh(
+                    pages = listOf("a1", "b1"),
+                    prepend = NotLoading.Complete
+                ),
+                drop(loadType = PREPEND, minPageOffset = 0, maxPageOffset = 0)
+            ).insertEventSeparators(LETTER_SEPARATOR_GENERATOR).toList()
+        ).isEqualTo(
             listOf(
                 Refresh(
                     pages = listOf(
                         TransformablePage(
-                            originalPageOffset = 0,
+                            originalPageOffsets = intArrayOf(0),
                             data = listOf("A"),
-                            originalPageSize = 1,
-                            originalIndices = listOf(0)
+                            hintOriginalPageOffset = 0,
+                            hintOriginalIndices = listOf(0)
                         ),
                         TransformablePage(
                             originalPageOffset = 0,
-                            data = listOf("a1"),
-                            originalPageSize = 1,
-                            originalIndices = null
+                            data = listOf("a1")
                         ),
                         TransformablePage(
-                            originalPageOffset = 1,
+                            originalPageOffsets = intArrayOf(0, 1),
                             data = listOf("B"),
-                            originalPageSize = 1,
-                            originalIndices = listOf(0)
+                            hintOriginalPageOffset = 1,
+                            hintOriginalIndices = listOf(0)
                         ),
                         TransformablePage(
                             originalPageOffset = 1,
-                            data = listOf("b1"),
-                            originalPageSize = 1,
-                            originalIndices = null
+                            data = listOf("b1")
                         )
                     ),
                     placeholdersBefore = 0,
                     placeholdersAfter = 1,
                     combinedLoadStates = localLoadStatesOf(prependLocal = NotLoading.Complete)
                 ),
-                drop(loadType = PREPEND, count = 3)
-            ),
-            flowOf(
-                refresh(
-                    pages = listOf("a1", "b1"),
-                    prepend = NotLoading.Complete
-                ),
-                drop(loadType = PREPEND, count = 1)
-            ).insertEventSeparators(LETTER_SEPARATOR_GENERATOR).toList()
+                drop(loadType = PREPEND, minPageOffset = 0, maxPageOffset = 0)
+            )
         )
     }
 
     @Test
     fun refreshEmptyEndDropFull() = runBlockingTest {
         // when end terminal separator is inserted, we need to drop count*2 + 1
-        assertEvents(
+        assertThat(
+            flowOf(
+                refresh(
+                    pages = listOf("a1", "b1"),
+                    append = NotLoading.Complete
+                ),
+                drop(loadType = APPEND, minPageOffset = 0, maxPageOffset = 0)
+            ).insertEventSeparators(LETTER_SEPARATOR_GENERATOR).toList()
+        ).isEqualTo(
             listOf(
                 Refresh(
                     pages = listOf(
                         TransformablePage(
                             originalPageOffset = 0,
-                            data = listOf("a1"),
-                            originalPageSize = 1,
-                            originalIndices = null
+                            data = listOf("a1")
                         ),
                         TransformablePage(
-                            originalPageOffset = 1,
+                            originalPageOffsets = intArrayOf(0, 1),
                             data = listOf("B"),
-                            originalPageSize = 1,
-                            originalIndices = listOf(0)
+                            hintOriginalPageOffset = 1,
+                            hintOriginalIndices = listOf(0)
                         ),
                         TransformablePage(
                             originalPageOffset = 1,
-                            data = listOf("b1"),
-                            originalPageSize = 1,
-                            originalIndices = null
+                            data = listOf("b1")
                         ),
                         TransformablePage(
-                            originalPageOffset = 1,
+                            originalPageOffsets = intArrayOf(1),
                             data = listOf("END"),
-                            originalPageSize = 1,
-                            originalIndices = listOf(0)
+                            hintOriginalPageOffset = 1,
+                            hintOriginalIndices = listOf(0)
                         )
                     ),
                     placeholdersBefore = 0,
                     placeholdersAfter = 1,
                     combinedLoadStates = localLoadStatesOf(appendLocal = NotLoading.Complete)
                 ),
-                drop(loadType = APPEND, count = 3)
-            ),
-            flowOf(
-                refresh(
-                    pages = listOf("a1", "b1"),
-                    append = NotLoading.Complete
-                ),
-                drop(loadType = APPEND, count = 1)
-            ).insertEventSeparators(LETTER_SEPARATOR_GENERATOR).toList()
+                drop(loadType = APPEND, minPageOffset = 0, maxPageOffset = 0)
+            )
         )
     }
 
@@ -630,6 +589,396 @@
         )
     }
 
+    @Test
+    fun refreshEmptyPagesExceptOne() = runBlockingTest {
+        assertThat(
+            flowOf(
+                Refresh(
+                    pages = listOf(
+                        listOf(),
+                        listOf(),
+                        listOf("a2", "b1"),
+                        listOf(),
+                        listOf()
+                    ).toTransformablePages(),
+                    placeholdersBefore = 0,
+                    placeholdersAfter = 1,
+                    combinedLoadStates = localLoadStatesOf()
+                )
+            ).insertEventSeparators(LETTER_SEPARATOR_GENERATOR).toList()
+        ).isEqualTo(
+            listOf(
+                Refresh(
+                    pages = listOf(
+                        TransformablePage(
+                            originalPageOffset = 0,
+                            data = listOf()
+                        ),
+                        TransformablePage(
+                            originalPageOffset = 1,
+                            data = listOf()
+                        ),
+                        TransformablePage(
+                            originalPageOffsets = intArrayOf(2),
+                            data = listOf("a2", "B", "b1"),
+                            hintOriginalPageOffset = 2,
+                            hintOriginalIndices = listOf(0, 1, 1)
+                        ),
+                        TransformablePage(
+                            originalPageOffset = 3,
+                            data = listOf()
+                        ),
+                        TransformablePage(
+                            originalPageOffset = 4,
+                            data = listOf()
+                        )
+                    ),
+                    placeholdersBefore = 0,
+                    placeholdersAfter = 1,
+                    combinedLoadStates = localLoadStatesOf()
+                )
+            )
+        )
+    }
+
+    @Test
+    fun refreshSparsePages() = runBlockingTest {
+        assertThat(
+            flowOf(
+                Refresh(
+                    pages = listOf(
+                        listOf(),
+                        listOf("a2", "b1"),
+                        listOf(),
+                        listOf("c1", "c2"),
+                        listOf()
+                    ).toTransformablePages(),
+                    placeholdersBefore = 0,
+                    placeholdersAfter = 1,
+                    combinedLoadStates = localLoadStatesOf()
+                )
+            ).insertEventSeparators(LETTER_SEPARATOR_GENERATOR).toList()
+        ).isEqualTo(
+            listOf(
+                Refresh(
+                    pages = listOf(
+                        TransformablePage(
+                            originalPageOffset = 0,
+                            data = listOf()
+                        ),
+                        TransformablePage(
+                            originalPageOffsets = intArrayOf(1),
+                            data = listOf("a2", "B", "b1"),
+                            hintOriginalPageOffset = 1,
+                            hintOriginalIndices = listOf(0, 1, 1)
+                        ),
+                        TransformablePage(
+                            originalPageOffset = 2,
+                            data = listOf()
+                        ),
+                        TransformablePage(
+                            originalPageOffsets = intArrayOf(1, 3),
+                            data = listOf("C"),
+                            hintOriginalPageOffset = 3,
+                            hintOriginalIndices = listOf(0)
+                        ),
+                        TransformablePage(
+                            originalPageOffset = 3,
+                            data = listOf("c1", "c2")
+                        ),
+                        TransformablePage(
+                            originalPageOffset = 4,
+                            data = listOf()
+                        )
+                    ),
+                    placeholdersBefore = 0,
+                    placeholdersAfter = 1,
+                    combinedLoadStates = localLoadStatesOf()
+                )
+            )
+        )
+    }
+
+    @Test
+    fun prependEmptyPagesExceptOne() = runBlockingTest {
+        val refresh = Refresh(
+            pages = listOf(
+                listOf("c1", "c2")
+            ).toTransformablePages(),
+            placeholdersBefore = 2,
+            placeholdersAfter = 0,
+            combinedLoadStates = localLoadStatesOf()
+        )
+
+        assertThat(
+            flowOf(
+                refresh,
+                Prepend(
+                    pages = listOf(
+                        listOf(),
+                        listOf(),
+                        listOf("a1", "b1"),
+                        listOf(),
+                        listOf()
+                    ).toTransformablePages(5),
+                    placeholdersBefore = 0,
+                    combinedLoadStates = localLoadStatesOf()
+                )
+            ).insertEventSeparators(LETTER_SEPARATOR_GENERATOR).toList()
+        ).isEqualTo(
+            listOf(
+                refresh,
+                Prepend(
+                    pages = listOf(
+                        TransformablePage(
+                            originalPageOffset = -5,
+                            data = listOf()
+                        ),
+                        TransformablePage(
+                            originalPageOffset = -4,
+                            data = listOf()
+                        ),
+                        TransformablePage(
+                            originalPageOffsets = intArrayOf(-3),
+                            data = listOf("a1", "B", "b1"),
+                            hintOriginalPageOffset = -3,
+                            hintOriginalIndices = listOf(0, 1, 1)
+                        ),
+                        TransformablePage(
+                            originalPageOffsets = intArrayOf(-3, 0),
+                            data = listOf("C"),
+                            hintOriginalPageOffset = -3,
+                            hintOriginalIndices = listOf(1)
+                        ),
+                        TransformablePage(
+                            originalPageOffset = -2,
+                            data = listOf()
+                        ),
+                        TransformablePage(
+                            originalPageOffset = -1,
+                            data = listOf()
+                        )
+                    ),
+                    placeholdersBefore = 0,
+                    combinedLoadStates = localLoadStatesOf()
+                )
+            )
+        )
+    }
+
+    @Test
+    fun prependSparsePages() = runBlockingTest {
+        val refresh = Refresh(
+            pages = listOf(
+                listOf("d1", "d2")
+            ).toTransformablePages(),
+            placeholdersBefore = 0,
+            placeholdersAfter = 4,
+            combinedLoadStates = localLoadStatesOf()
+        )
+
+        assertThat(
+            flowOf(
+                refresh,
+                Prepend(
+                    pages = listOf(
+                        listOf(),
+                        listOf("a1", "b1"),
+                        listOf(),
+                        listOf("c1", "c2"),
+                        listOf()
+                    ).toTransformablePages(5),
+                    placeholdersBefore = 0,
+                    combinedLoadStates = localLoadStatesOf()
+                )
+            ).insertEventSeparators(LETTER_SEPARATOR_GENERATOR).toList()
+        ).isEqualTo(
+            listOf(
+                refresh,
+                Prepend(
+                    pages = listOf(
+                        TransformablePage(
+                            originalPageOffset = -5,
+                            data = listOf()
+                        ),
+                        TransformablePage(
+                            originalPageOffsets = intArrayOf(-4),
+                            data = listOf("a1", "B", "b1"),
+                            hintOriginalPageOffset = -4,
+                            hintOriginalIndices = listOf(0, 1, 1)
+                        ),
+                        TransformablePage(
+                            originalPageOffset = -3,
+                            data = listOf()
+                        ),
+                        TransformablePage(
+                            originalPageOffsets = intArrayOf(-4, -2),
+                            data = listOf("C"),
+                            hintOriginalPageOffset = -4,
+                            hintOriginalIndices = listOf(1)
+                        ),
+                        TransformablePage(
+                            originalPageOffset = -2,
+                            data = listOf("c1", "c2")
+                        ),
+                        TransformablePage(
+                            originalPageOffsets = intArrayOf(-2, 0),
+                            data = listOf("D"),
+                            hintOriginalPageOffset = -2,
+                            hintOriginalIndices = listOf(1)
+                        ),
+                        TransformablePage(
+                            originalPageOffset = -1,
+                            data = listOf()
+                        )
+                    ),
+                    placeholdersBefore = 0,
+                    combinedLoadStates = localLoadStatesOf()
+                )
+            )
+        )
+    }
+
+    @Test
+    fun appendEmptyPagesExceptOne() = runBlockingTest {
+        val refresh = Refresh(
+            pages = listOf(
+                listOf("a1", "a2")
+            ).toTransformablePages(),
+            placeholdersBefore = 0,
+            placeholdersAfter = 2,
+            combinedLoadStates = localLoadStatesOf()
+        )
+
+        assertThat(
+            flowOf(
+                refresh,
+                Append(
+                    pages = listOf(
+                        listOf(),
+                        listOf(),
+                        listOf("b1", "c1"),
+                        listOf(),
+                        listOf()
+                    ).toTransformablePages(-1),
+                    placeholdersAfter = 0,
+                    combinedLoadStates = localLoadStatesOf()
+                )
+            ).insertEventSeparators(LETTER_SEPARATOR_GENERATOR).toList()
+        ).isEqualTo(
+            listOf(
+                refresh,
+                Append(
+                    pages = listOf(
+                        TransformablePage(
+                            originalPageOffset = 1,
+                            data = listOf()
+                        ),
+                        TransformablePage(
+                            originalPageOffset = 2,
+                            data = listOf()
+                        ),
+                        TransformablePage(
+                            originalPageOffsets = intArrayOf(0, 3),
+                            data = listOf("B"),
+                            hintOriginalPageOffset = 3,
+                            hintOriginalIndices = listOf(0)
+                        ),
+                        TransformablePage(
+                            originalPageOffsets = intArrayOf(3),
+                            data = listOf("b1", "C", "c1"),
+                            hintOriginalPageOffset = 3,
+                            hintOriginalIndices = listOf(0, 1, 1)
+                        ),
+                        TransformablePage(
+                            originalPageOffset = 4,
+                            data = listOf()
+                        ),
+                        TransformablePage(
+                            originalPageOffset = 5,
+                            data = listOf()
+                        )
+                    ),
+                    placeholdersAfter = 0,
+                    combinedLoadStates = localLoadStatesOf()
+                )
+            )
+        )
+    }
+
+    @Test
+    fun appendSparsePages() = runBlockingTest {
+        val refresh = Refresh(
+            pages = listOf(
+                listOf("a1", "a2")
+            ).toTransformablePages(),
+            placeholdersBefore = 0,
+            placeholdersAfter = 4,
+            combinedLoadStates = localLoadStatesOf()
+        )
+
+        assertThat(
+            flowOf(
+                refresh,
+                Append(
+                    pages = listOf(
+                        listOf(),
+                        listOf("b1", "c1"),
+                        listOf(),
+                        listOf("d1", "d2"),
+                        listOf()
+                    ).toTransformablePages(-1),
+                    placeholdersAfter = 0,
+                    combinedLoadStates = localLoadStatesOf()
+                )
+            ).insertEventSeparators(LETTER_SEPARATOR_GENERATOR).toList()
+        ).isEqualTo(
+            listOf(
+                refresh,
+                Append(
+                    pages = listOf(
+                        TransformablePage(
+                            originalPageOffset = 1,
+                            data = listOf()
+                        ),
+                        TransformablePage(
+                            originalPageOffsets = intArrayOf(0, 2),
+                            data = listOf("B"),
+                            hintOriginalPageOffset = 2,
+                            hintOriginalIndices = listOf(0)
+                        ),
+                        TransformablePage(
+                            originalPageOffsets = intArrayOf(2),
+                            data = listOf("b1", "C", "c1"),
+                            hintOriginalPageOffset = 2,
+                            hintOriginalIndices = listOf(0, 1, 1)
+                        ),
+                        TransformablePage(
+                            originalPageOffset = 3,
+                            data = listOf()
+                        ),
+                        TransformablePage(
+                            originalPageOffsets = intArrayOf(2, 4),
+                            data = listOf("D"),
+                            hintOriginalPageOffset = 4,
+                            hintOriginalIndices = listOf(0)
+                        ),
+                        TransformablePage(
+                            originalPageOffset = 4,
+                            data = listOf("d1", "d2")
+                        ),
+                        TransformablePage(
+                            originalPageOffset = 5,
+                            data = listOf()
+                        )
+                    ),
+                    placeholdersAfter = 0,
+                    combinedLoadStates = localLoadStatesOf()
+                )
+            )
+        )
+    }
+
     companion object {
         /**
          * Creates an upper-case letter at the beginning of each section of strings that start
diff --git a/paging/common/src/test/kotlin/androidx/paging/SeparatorsWithRemoteMediatorTest.kt b/paging/common/src/test/kotlin/androidx/paging/SeparatorsWithRemoteMediatorTest.kt
index fb4dae7..9a314ab 100644
--- a/paging/common/src/test/kotlin/androidx/paging/SeparatorsWithRemoteMediatorTest.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/SeparatorsWithRemoteMediatorTest.kt
@@ -170,10 +170,10 @@
     originalPageOffset: Int,
     data: List<String>
 ) = TransformablePage(
-    originalPageOffset = originalPageOffset,
+    originalPageOffsets = intArrayOf(originalPageOffset),
     data = data,
-    originalPageSize = data.size,
-    originalIndices = data.fold(mutableListOf()) { acc, s ->
+    hintOriginalPageOffset = originalPageOffset,
+    hintOriginalIndices = data.fold(mutableListOf()) { acc, s ->
         acc.apply {
             add(when {
                 acc.isEmpty() -> 0
diff --git a/paging/common/src/test/kotlin/androidx/paging/TestPagingSourceExt.kt b/paging/common/src/test/kotlin/androidx/paging/TestPagingSourceExt.kt
index d2b8fa7..da362a1 100644
--- a/paging/common/src/test/kotlin/androidx/paging/TestPagingSourceExt.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/TestPagingSourceExt.kt
@@ -68,8 +68,6 @@
 ) = listOf(
     TransformablePage(
         originalPageOffset = pageOffset,
-        data = ITEMS.slice(range),
-        originalPageSize = range.count(),
-        originalIndices = null
+        data = ITEMS.slice(range)
     )
 )
diff --git a/preference/preference/src/main/java/androidx/preference/PreferenceDialogFragment.java b/preference/preference/src/main/java/androidx/preference/PreferenceDialogFragment.java
index 5344523..cec547f 100644
--- a/preference/preference/src/main/java/androidx/preference/PreferenceDialogFragment.java
+++ b/preference/preference/src/main/java/androidx/preference/PreferenceDialogFragment.java
@@ -26,11 +26,13 @@
 import android.graphics.Canvas;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
+import android.os.Build;
 import android.os.Bundle;
 import android.text.TextUtils;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.Window;
+import android.view.WindowInsets;
 import android.view.WindowManager;
 import android.widget.TextView;
 
@@ -223,10 +225,21 @@
 
     /**
      * Sets the required flags on the dialog window to enable input method window to show up.
+     * <p>
+     * Note that starting from Android R, the new WindowInsets API supports showing soft-input
+     * on-demand, so there is no longer a need to rely on the
+     * {@link WindowManager.LayoutParams#SOFT_INPUT_STATE_ALWAYS_VISIBLE} flag to show the
+     * soft-input when there is no focused editor.</p>
      */
     private void requestInputMethod(Dialog dialog) {
-        Window window = dialog.getWindow();
-        window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
+        // TODO:(b/163914595) Remove the dependency of STATE_ALWAYS_VISIBLE for pre-R.
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
+            Window window = dialog.getWindow();
+            window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
+        } else {
+            dialog.getWindow().getDecorView().getWindowInsetsController()
+                    .show(WindowInsets.Type.ime());
+        }
     }
 
     /**
diff --git a/preference/preference/src/main/java/androidx/preference/PreferenceDialogFragmentCompat.java b/preference/preference/src/main/java/androidx/preference/PreferenceDialogFragmentCompat.java
index fbf4b728..c628b8d 100644
--- a/preference/preference/src/main/java/androidx/preference/PreferenceDialogFragmentCompat.java
+++ b/preference/preference/src/main/java/androidx/preference/PreferenceDialogFragmentCompat.java
@@ -25,10 +25,12 @@
 import android.graphics.Canvas;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
+import android.os.Build;
 import android.os.Bundle;
 import android.text.TextUtils;
 import android.view.View;
 import android.view.Window;
+import android.view.WindowInsets;
 import android.view.WindowManager;
 import android.widget.TextView;
 
@@ -203,10 +205,21 @@
 
     /**
      * Sets the required flags on the dialog window to enable input method window to show up.
+     * <p>
+     * Note that starting from Android R, the new WindowInsets API supports showing soft-input
+     * on-demand, so there is no longer a need to rely on the
+     * {@link WindowManager.LayoutParams#SOFT_INPUT_STATE_ALWAYS_VISIBLE} flag to show the
+     * soft-input when there is no focused editor.</p>
      */
     private void requestInputMethod(Dialog dialog) {
-        Window window = dialog.getWindow();
-        window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
+            // TODO:(b/163914595) Remove the dependency of STATE_ALWAYS_VISIBLE for pre-R.
+            Window window = dialog.getWindow();
+            window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
+        } else {
+            dialog.getWindow().getDecorView().getWindowInsetsController()
+                    .show(WindowInsets.Type.ime());
+        }
     }
 
     /**
diff --git a/room/runtime/src/main/java/androidx/room/RoomDatabase.java b/room/runtime/src/main/java/androidx/room/RoomDatabase.java
index d1448a5..7e0f442 100644
--- a/room/runtime/src/main/java/androidx/room/RoomDatabase.java
+++ b/room/runtime/src/main/java/androidx/room/RoomDatabase.java
@@ -646,6 +646,9 @@
          * pre-packaged database schema utilizing the exported schema files generated when
          * {@link Database#exportSchema()} is enabled.
          * <p>
+         * The {@link Callback#onOpen(SupportSQLiteDatabase)} method can be used as an indicator
+         * that the pre-packaged database was successfully opened by Room and can be cleaned up.
+         * <p>
          * This method is not supported for an in memory database {@link Builder}.
          *
          * @param databaseFile The database file.
@@ -669,6 +672,9 @@
          * pre-packaged database schema utilizing the exported schema files generated when
          * {@link Database#exportSchema()} is enabled.
          * <p>
+         * The {@link Callback#onOpen(SupportSQLiteDatabase)} method can be used as an indicator
+         * that the pre-packaged database was successfully opened by Room and can be cleaned up.
+         * <p>
          * This method is not supported for an in memory database {@link Builder}.
          *
          * @param databaseFile The database file.
@@ -692,17 +698,23 @@
          * <p>
          * This is useful for processing compressed database files. Room does not open the
          * pre-packaged database, instead it copies it into the internal app database folder, and
-         * then open it.
+         * then open it. The {@link InputStream} will be closed once Room is done consuming it.
          * <p>
          * The pre-packaged database schema will be validated. It might be best to create your
          * pre-packaged database schema utilizing the exported schema files generated when
          * {@link Database#exportSchema()} is enabled.
          * <p>
-         * This method is not supported for an in memory database {@link Builder}. The underlying
-         * {@link InputStream} will be closed.
+         * The {@link Callback#onOpen(SupportSQLiteDatabase)} method can be used as an indicator
+         * that the pre-packaged database was successfully opened by Room and can be cleaned up.
+         * <p>
+         * This method is not supported for an in memory database {@link Builder}.
          *
          * @param inputStreamCallable A callable that returns an InputStream from which to copy
-         *                            the database.
+         *                            the database. The callable will be invoked in a thread from
+         *                            the Executor set via {@link #setQueryExecutor(Executor)}. The
+         *                            callable is only invoked if Room needs to create and open the
+         *                            database from the pre-package database, usually the first time
+         *                            it is created or during a destructive migration.
          *
          * @return This {@link Builder} instance.
          */
@@ -720,17 +732,23 @@
          * <p>
          * This is useful for processing compressed database files. Room does not open the
          * pre-packaged database, instead it copies it into the internal app database folder, and
-         * then open it.
+         * then open it. The {@link InputStream} will be closed once Room is done consuming it.
          * <p>
          * The pre-packaged database schema will be validated. It might be best to create your
          * pre-packaged database schema utilizing the exported schema files generated when
          * {@link Database#exportSchema()} is enabled.
          * <p>
-         * This method is not supported for an in memory database {@link Builder}. The underlying
-         * {@link InputStream} will be closed.
+         * The {@link Callback#onOpen(SupportSQLiteDatabase)} method can be used as an indicator
+         * that the pre-packaged database was successfully opened by Room and can be cleaned up.
+         * <p>
+         * This method is not supported for an in memory database {@link Builder}.
          *
          * @param inputStreamCallable A callable that returns an InputStream from which to copy
-         *                            the database.
+         *                            the database. The callable will be invoked in a thread from
+         *                            the Executor set via {@link #setQueryExecutor(Executor)}. The
+         *                            callable is only invoked if Room needs to create and open the
+         *                            database from the pre-package database, usually the first time
+         *                            it is created or during a destructive migration.
          * @param callback The pre-packaged callback.
          *
          * @return This {@link Builder} instance.
diff --git a/samples/Support7Demos/src/main/java/com/example/android/supportv7/widget/selection/fancy/DemoAdapter.java b/samples/Support7Demos/src/main/java/com/example/android/supportv7/widget/selection/fancy/DemoAdapter.java
index 94be45d..94131b5 100644
--- a/samples/Support7Demos/src/main/java/com/example/android/supportv7/widget/selection/fancy/DemoAdapter.java
+++ b/samples/Support7Demos/src/main/java/com/example/android/supportv7/widget/selection/fancy/DemoAdapter.java
@@ -20,18 +20,16 @@
 
 import android.content.Context;
 import android.net.Uri;
-import android.view.LayoutInflater;
-import android.view.View;
 import android.view.ViewGroup;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.core.util.Predicate;
 import androidx.recyclerview.selection.ItemKeyProvider;
 import androidx.recyclerview.selection.SelectionTracker;
 import androidx.recyclerview.widget.RecyclerView;
 
 import com.example.android.supportv7.Cheeses;
-import com.example.android.supportv7.R;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -47,13 +45,15 @@
     // Our list of thingies. Our DemoHolder subclasses extract display
     // values directly from the Uri, so we only need this simple list.
     // The list also contains entries for alphabetical section headers.
-    private final List<Uri> mCheeses;
+    private final List<Uri> mCheeses = new ArrayList<>();
+    private boolean mSmallItemLayout;
+    private boolean mAllCheesesEnabled;
 
     // This default implementation must be replaced
     // with a real implementation in #bindSelectionHelper.
-    private SelectionTest mSelTest = new SelectionTest() {
+    private Predicate<Uri> mIsSelectedTest = new Predicate<Uri>() {
         @Override
-        public boolean isSelected(Uri id) {
+        public boolean test(Uri key) {
             throw new IllegalStateException(
                     "Adapter must be initialized with SelectionTracker");
         }
@@ -61,7 +61,6 @@
 
     DemoAdapter(Context context) {
         mContext = context;
-        mCheeses = createCheeseList("CheeseKindom");
         mKeyProvider = new KeyProvider(mCheeses);
 
         // In the fancy edition of selection support we supply access to stable
@@ -77,22 +76,14 @@
     // Glue together SelectionTracker and the adapter.
     public void bindSelectionTracker(final SelectionTracker<Uri> tracker) {
         checkArgument(tracker != null);
-        mSelTest = new SelectionTest() {
+        mIsSelectedTest = new Predicate<Uri>() {
             @Override
-            public boolean isSelected(Uri id) {
-                return tracker.isSelected(id);
+            public boolean test(Uri key) {
+                return tracker.isSelected(key);
             }
         };
     }
 
-    void loadData() {
-        onDataReady();
-    }
-
-    private void onDataReady() {
-        notifyDataSetChanged();
-    }
-
     @Override
     public int getItemCount() {
         return mCheeses.size();
@@ -105,22 +96,21 @@
 
     @Override
     public void onBindViewHolder(@NonNull DemoHolder holder, int position) {
-        if (holder instanceof DemoHeaderHolder) {
-            Uri uri = mKeyProvider.getKey(position);
-            ((DemoHeaderHolder) holder).update(uri.getPathSegments().get(0));
-        } else if (holder instanceof DemoItemHolder) {
-            Uri uri = mKeyProvider.getKey(position);
-            ((DemoItemHolder) holder).update(uri, uri.getPathSegments().get(1),
-                    mSelTest.isSelected(uri));
+        Uri uri = mKeyProvider.getKey(position);
+        holder.update(uri);
+        if (holder instanceof DemoItemHolder) {
+            DemoItemHolder itemHolder = (DemoItemHolder) holder;
+            itemHolder.setSelected(mIsSelectedTest.test(uri));
+            itemHolder.setSmallLayoutMode(mSmallItemLayout);
         }
     }
 
     @Override
     public int getItemViewType(int position) {
-        Uri key = mKeyProvider.getKey(position);
-        if (key.getPathSegments().size() == 1) {
+        Uri uri = mKeyProvider.getKey(position);
+        if (Uris.isGroup(uri)) {
             return TYPE_HEADER;
-        } else if (key.getPathSegments().size() == 2) {
+        } else if (Uris.isCheese(uri)) {
             return TYPE_ITEM;
         }
 
@@ -131,57 +121,32 @@
     public DemoHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
         switch (viewType) {
             case TYPE_HEADER:
-                return new DemoHeaderHolder(
-                        inflateLayout(mContext, parent, R.layout.selection_demo_list_header));
+                return new DemoHeaderHolder(mContext, parent);
             case TYPE_ITEM:
-                return new DemoItemHolder(
-                        inflateLayout(mContext, parent, R.layout.selection_demo_list_item));
+                return new DemoItemHolder(mContext, parent);
         }
         throw new RuntimeException("Unsupported view type" + viewType);
     }
 
-    @SuppressWarnings("TypeParameterUnusedInFormals")  // Convenience to avoid clumsy cast.
-    private static <V extends View> V inflateLayout(
-            Context context, ViewGroup parent, int layout) {
-
-        return (V) LayoutInflater.from(context).inflate(layout, parent, false);
-    }
-
     // Creates a list of cheese Uris and section header Uris.
-    private static List<Uri> createCheeseList(String authority) {
-        List<Uri> cheeses = new ArrayList<>();
-        char section = '-';  // any ol' value other than 'a' will do the trick here.
+    private void populateCheeses(int maxItemsPerGroup) {
+        String group = "-";  // any ol' value other than 'a' will do the trick here.
+        int itemsInGroup = 0;
 
         for (String cheese : Cheeses.sCheeseStrings) {
-            char leadingChar = cheese.toLowerCase().charAt(0);
+            String leadingChar = Character.toString(cheese.toLowerCase().charAt(0));
 
             // When we find a new leading character insert an artificial
             // cheese header
-            if (leadingChar != section) {
-                section = leadingChar;
-                Uri headerUri = new Uri.Builder()
-                        .scheme("content")
-                        .encodedAuthority(authority)
-                        .appendPath(Character.toString(section))
-                        .build();
-
-                cheeses.add(headerUri);
+            if (!leadingChar.equals(group)) {
+                group = leadingChar;
+                itemsInGroup = 0;
+                mCheeses.add(Uris.forGroup(group));
             }
-
-            Uri itemUri = new Uri.Builder()
-                    .scheme("content")
-                    .encodedAuthority(authority)
-                    .appendPath(Character.toString(section))
-                    .appendPath(cheese)
-                    .build();
-            cheeses.add(itemUri);
+            if (++itemsInGroup <= maxItemsPerGroup) {
+                mCheeses.add(Uris.forCheese(group, cheese));
+            }
         }
-
-        return cheeses;
-    }
-
-    private interface SelectionTest {
-        boolean isSelected(Uri id);
     }
 
     public boolean removeItem(Uri key) {
@@ -195,6 +160,30 @@
         return removed != null;
     }
 
+    void enableSmallItemLayout(boolean enabled) {
+        mSmallItemLayout = enabled;
+    }
+
+    void enableAllCheeses(boolean enabled) {
+        mAllCheesesEnabled = enabled;
+    }
+
+    boolean smallItemLayoutEnabled() {
+        return mSmallItemLayout;
+    }
+
+    boolean allCheesesEnabled() {
+        return mAllCheesesEnabled;
+    }
+
+
+
+    void refresh() {
+        mCheeses.clear();
+        populateCheeses(mAllCheesesEnabled ? Integer.MAX_VALUE : 5);
+        notifyDataSetChanged();
+    }
+
     /**
      * When ever possible provide the selection library with a
      * "SCOPED_MAPPED" ItemKeyProvider. This enables the selection
@@ -211,13 +200,13 @@
 
         private final List<Uri> mData;
 
-        KeyProvider(List<Uri> cheeses) {
+        KeyProvider(List<Uri> data) {
             // Advise the world we can supply ids/position for any item at any time,
             // not just when visible in RecyclerView.
             // This enables fancy stuff especially helpful to users with pointy
             // devices like Chromebooks, or tablets with touch pads
             super(SCOPE_MAPPED);
-            mData = cheeses;
+            mData = data;
         }
 
         @Override
@@ -228,13 +217,7 @@
         @Override
         public int getPosition(@NonNull Uri key) {
             int position = Collections.binarySearch(mData, key);
-            // position is insertion point if key is missing.
-            // Since the insertion point could be end of the list + 1
-            // both verify the position is in bounds, and that the value
-            // at position is the same as the key.
-            return position >= 0 && position <= mData.size() - 1 && key.equals(mData.get(position))
-                    ? position
-                    : RecyclerView.NO_POSITION;
+            return position >= 0 ? position : RecyclerView.NO_POSITION;
         }
     }
 }
diff --git a/samples/Support7Demos/src/main/java/com/example/android/supportv7/widget/selection/fancy/DemoHeaderHolder.java b/samples/Support7Demos/src/main/java/com/example/android/supportv7/widget/selection/fancy/DemoHeaderHolder.java
index be78dd0..0e64018 100644
--- a/samples/Support7Demos/src/main/java/com/example/android/supportv7/widget/selection/fancy/DemoHeaderHolder.java
+++ b/samples/Support7Demos/src/main/java/com/example/android/supportv7/widget/selection/fancy/DemoHeaderHolder.java
@@ -15,26 +15,32 @@
  */
 package com.example.android.supportv7.widget.selection.fancy;
 
-import android.view.View;
+import android.content.Context;
+import android.net.Uri;
+import android.view.ViewGroup;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
-import androidx.annotation.Nullable;
+import androidx.annotation.NonNull;
 
 import com.example.android.supportv7.R;
 
 final class DemoHeaderHolder extends DemoHolder {
 
-    private static final String HEADER_TAG = "I'm a header";
     final TextView mLabel;
 
-    DemoHeaderHolder(LinearLayout layout) {
+    DemoHeaderHolder(@NonNull Context context, @NonNull ViewGroup parent) {
+        this(inflateLayout(context, parent, R.layout.selection_demo_list_header));
+    }
+
+    private DemoHeaderHolder(LinearLayout layout) {
         super(layout);
-        layout.setTag(HEADER_TAG);
         mLabel = layout.findViewById(R.id.label);
     }
 
-    void update(String label) {
+    @Override
+    void update(@NonNull Uri uri) {
+        String label = Uris.getGroup(uri);
         mLabel.setText(label.toUpperCase() + label + label + "...");
     }
 
@@ -42,8 +48,4 @@
     public String toString() {
         return "Header{name:" + mLabel.getText() + "}";
     }
-
-    static boolean isHeader(@Nullable View view) {
-        return view == null ? false : HEADER_TAG.equals(view.getTag());
-    }
 }
diff --git a/samples/Support7Demos/src/main/java/com/example/android/supportv7/widget/selection/fancy/DemoHolder.java b/samples/Support7Demos/src/main/java/com/example/android/supportv7/widget/selection/fancy/DemoHolder.java
index 0c6b999..fd8d494 100644
--- a/samples/Support7Demos/src/main/java/com/example/android/supportv7/widget/selection/fancy/DemoHolder.java
+++ b/samples/Support7Demos/src/main/java/com/example/android/supportv7/widget/selection/fancy/DemoHolder.java
@@ -16,12 +16,25 @@
 
 package com.example.android.supportv7.widget.selection.fancy;
 
+import android.content.Context;
+import android.net.Uri;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
 import android.widget.LinearLayout;
 
+import androidx.annotation.NonNull;
 import androidx.recyclerview.widget.RecyclerView;
 
 abstract class DemoHolder extends RecyclerView.ViewHolder {
     DemoHolder(LinearLayout layout) {
         super(layout);
     }
+
+    abstract void update(@NonNull Uri uri);
+
+    @SuppressWarnings("TypeParameterUnusedInFormals")  // Convenience to avoid clumsy cast.
+    static <V extends View> V inflateLayout(Context context, ViewGroup parent, int layout) {
+        return (V) LayoutInflater.from(context).inflate(layout, parent, false);
+    }
 }
diff --git a/samples/Support7Demos/src/main/java/com/example/android/supportv7/widget/selection/fancy/DemoItemHolder.java b/samples/Support7Demos/src/main/java/com/example/android/supportv7/widget/selection/fancy/DemoItemHolder.java
index 176fb3d..88e932e 100644
--- a/samples/Support7Demos/src/main/java/com/example/android/supportv7/widget/selection/fancy/DemoItemHolder.java
+++ b/samples/Support7Demos/src/main/java/com/example/android/supportv7/widget/selection/fancy/DemoItemHolder.java
@@ -15,12 +15,16 @@
  */
 package com.example.android.supportv7.widget.selection.fancy;
 
+import android.content.Context;
 import android.graphics.Rect;
 import android.net.Uri;
 import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
+import androidx.annotation.Dimension;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.recyclerview.selection.ItemDetailsLookup.ItemDetails;
@@ -36,7 +40,11 @@
 
     private @Nullable Uri mKey;
 
-    DemoItemHolder(LinearLayout layout) {
+    DemoItemHolder(@NonNull Context context, @NonNull ViewGroup parent) {
+        this(inflateLayout(context, parent, R.layout.selection_demo_list_item));
+    }
+
+    private DemoItemHolder(LinearLayout layout) {
         super(layout);
 
         mContainer = layout.findViewById(R.id.container);
@@ -71,13 +79,18 @@
         };
     }
 
-    void update(Uri key, String label, boolean selected) {
-        mKey = key;
-        mLabel.setText(label);
-        setSelected(selected);
+    @Override
+    void update(@NonNull Uri uri) {
+        mKey = uri;
+        mLabel.setText(Uris.getCheese(uri));
     }
 
-    private void setSelected(boolean selected) {
+    void setSmallLayoutMode(boolean small) {
+        mSelector.setVisibility(small ? View.GONE : View.VISIBLE);
+        mLabel.setTextSize(Dimension.SP, small ? 14f : 20f);
+    }
+
+    void setSelected(boolean selected) {
         mContainer.setActivated(selected);
         mSelector.setActivated(selected);
     }
@@ -108,8 +121,8 @@
 
     boolean inSelectRegion(MotionEvent e) {
         Rect iconRect = new Rect();
-        mSelector.getGlobalVisibleRect(iconRect);
-        return iconRect.contains((int) e.getRawX(), (int) e.getRawY());
+        return mSelector.getGlobalVisibleRect(iconRect)
+                && iconRect.contains((int) e.getRawX(), (int) e.getRawY());
     }
 
     ItemDetails<Uri> getItemDetails() {
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 5da1896..3be459a 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
@@ -72,7 +72,6 @@
     private SelectionTracker<Uri> mSelectionTracker;
 
     private GridLayoutManager mLayout;
-    private boolean mIterceptListenerEnabled = false;
     private boolean mSwipeDuringSelectionEnabled = false;
 
     @Override
@@ -82,12 +81,23 @@
         setContentView(R.layout.selection_demo_layout);
         mRecView = (RecyclerView) findViewById(R.id.list);
 
-        // Demo how to intercept touch events before selection tracker.
-        // In case you need to do something fancy that selection tracker
-        // might otherwise interfere with.
-        setupCustomTouchListener();
-
         mLayout = new GridLayoutManager(this, 1);
+
+        // Let our headers span any number of columns.
+        mLayout.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
+            @Override
+            public int getSpanSize(int position) {
+                switch(mAdapter.getItemViewType(position)){
+                    case DemoAdapter.TYPE_HEADER:
+                        return mLayout.getSpanCount();
+
+                    case DemoAdapter.TYPE_ITEM:
+                    default:
+                        return 1;
+                }
+            }
+        });
+
         mRecView.setLayoutManager(mLayout);
         mAdapter = new DemoAdapter(this);
         mRecView.setAdapter(mAdapter);
@@ -209,42 +219,31 @@
                 });
     }
 
-    // If you want to provided special handling of clicks on items
-    // in RecyclerView (respond to a play button, or show a menu
-    // when a three-dot menu is clicked) you can't just add an OnClickListener
-    // to the View.  This is because Selection lib installs an
-    // OnItemTouchListener w/ RecyclerView, and that listener eats
-    // up many of the touch/mouse events RecyclerView sends its way.
-    // To work around this install your own OnItemTouchListener *before*
-    // you build your SelectionTracker instance. That'll give your listener
-    // a chance to intercept events before Selection lib gobbles them up.
-    private void setupCustomTouchListener() {
-        mRecView.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() {
-            @Override
-            public boolean onInterceptTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) {
-                return mIterceptListenerEnabled
-                        && DemoHeaderHolder.isHeader(rv.findChildViewUnder(e.getX(), e.getY()));
-            }
-
-            @Override
-            public void onTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) {
-                toast(FancySelectionDemoActivity.this, "Clicked on a header!");
-            }
-
-            @Override
-            public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
-            }
-        });
-    }
-
     @Override
     protected void onSaveInstanceState(@NonNull Bundle state) {
         super.onSaveInstanceState(state);
         mSelectionTracker.onSaveInstanceState(state);
+        state.putBoolean("showAll", mAdapter.allCheesesEnabled());
+        state.putBoolean("gridLayout", mAdapter.smallItemLayoutEnabled());
+        state.putBoolean("enableSwipe", mSwipeDuringSelectionEnabled);
     }
 
-    private void updateFromSavedState(Bundle state) {
+    private void updateFromSavedState(@Nullable Bundle state) {
         mSelectionTracker.onRestoreInstanceState(state);
+
+        boolean showAll = false;
+        boolean gridLayout = false;
+        if (state == null) {
+            mSwipeDuringSelectionEnabled = true;
+        } else {
+            showAll = state.getBoolean("showAll");
+            gridLayout = state.getBoolean("gridLayout");
+            mSwipeDuringSelectionEnabled = state.getBoolean("enableSwipe");
+        }
+
+        mAdapter.enableAllCheeses(showAll);
+        mLayout.setSpanCount(gridLayout ? 2 : 1);
+        mAdapter.enableSmallItemLayout(gridLayout);
     }
 
     @Override
@@ -252,22 +251,41 @@
         boolean showMenu = super.onCreateOptionsMenu(menu);
         getMenuInflater().inflate(R.menu.selection_demo_actions, menu);
         for (int i = 0; i < menu.size(); i++) {
-            updateOptionFromMenu(menu.getItem(i));
+            MenuItem item = menu.getItem(i);
+            switch (item.getItemId()) {
+                case R.id.option_menu_more_cheese:
+                    item.setChecked(mAdapter.allCheesesEnabled());
+                    break;
+                case R.id.option_menu_grid_layout:
+                    item.setChecked(mAdapter.smallItemLayoutEnabled());
+                    break;
+                case R.id.option_menu_swipe_during_select:
+                    item.setChecked(mSwipeDuringSelectionEnabled);
+                    break;
+            }
         }
         return showMenu;
     }
 
     @Override
     public boolean onOptionsItemSelected(@NonNull MenuItem item) {
-        item.setChecked(!item.isChecked());
+        if (item.isCheckable()) {
+            item.setChecked(!item.isChecked());
+        }
         updateOptionFromMenu(item);
         return true;
     }
 
     private void updateOptionFromMenu(@NonNull MenuItem item) {
         switch (item.getItemId()) {
-            case R.id.option_menu_custom_listener:
-                mIterceptListenerEnabled = item.isChecked();
+            case R.id.option_menu_more_cheese:
+                mAdapter.enableAllCheeses(item.isChecked());
+                mAdapter.refresh();
+                break;
+            case R.id.option_menu_grid_layout:
+                mAdapter.enableSmallItemLayout(item.isChecked());
+                mLayout.setSpanCount(item.isChecked() ? 2 : 1);
+                mAdapter.refresh();
                 break;
             case R.id.option_menu_swipe_during_select:
                 mSwipeDuringSelectionEnabled = item.isChecked();
@@ -329,7 +347,7 @@
     @Override
     protected void onStart() {
         super.onStart();
-        mAdapter.loadData();
+        mAdapter.refresh();
     }
 
     // Tracking focus separately from explicit selection
diff --git a/samples/Support7Demos/src/main/java/com/example/android/supportv7/widget/selection/fancy/Uris.java b/samples/Support7Demos/src/main/java/com/example/android/supportv7/widget/selection/fancy/Uris.java
new file mode 100644
index 0000000..ca9cd57
--- /dev/null
+++ b/samples/Support7Demos/src/main/java/com/example/android/supportv7/widget/selection/fancy/Uris.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2020 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.android.supportv7.widget.selection.fancy;
+
+import android.net.Uri;
+
+import androidx.annotation.NonNull;
+import androidx.core.util.Preconditions;
+
+final class Uris {
+
+    private Uris() {}
+
+    static final String SCHEME = "content";
+    static final String AUTHORITY = "CheeseWorld";
+    static final String PARAM_GROUP = "g";
+    static final String PARAM_CHEESE = "c";
+
+    static @NonNull Uri forGroup(@NonNull String group) {
+        return new Uri.Builder()
+                .scheme(SCHEME)
+                .encodedAuthority(AUTHORITY)
+                .appendQueryParameter(PARAM_GROUP, group)
+                .build();
+    }
+
+    static @NonNull Uri forCheese(@NonNull String group, @NonNull String cheese) {
+        return new Uri.Builder()
+                .scheme(SCHEME)
+                .encodedAuthority(AUTHORITY)
+                .appendQueryParameter(PARAM_GROUP, group)
+                .appendQueryParameter(PARAM_CHEESE, cheese)
+                .build();
+    }
+
+    static boolean isGroup(@NonNull Uri uri) {
+        return !isCheese(uri);
+    }
+
+    static boolean isCheese(@NonNull Uri uri) {
+        return uri.getQueryParameter(PARAM_GROUP) != null
+                && uri.getQueryParameter(PARAM_CHEESE) != null;
+    }
+
+    static @NonNull String getGroup(@NonNull Uri uri) {
+        String group = uri.getQueryParameter(PARAM_GROUP);
+        Preconditions.checkArgument(group != null);
+        return group;
+    }
+
+    static @NonNull String getCheese(@NonNull Uri uri) {
+        Preconditions.checkArgument(isCheese(uri));
+        return uri.getQueryParameter(PARAM_CHEESE);
+    }
+}
diff --git a/samples/Support7Demos/src/main/res/color/selection_demo_item_selector.xml b/samples/Support7Demos/src/main/res/color/selection_demo_item_selector.xml
index c800127..3e6959d 100644
--- a/samples/Support7Demos/src/main/res/color/selection_demo_item_selector.xml
+++ b/samples/Support7Demos/src/main/res/color/selection_demo_item_selector.xml
@@ -17,8 +17,8 @@
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
     <item
         android:state_activated="false"
-        android:color="?android:attr/colorForeground"
-        android:alpha=".3"
+        android:color="@android:color/black"
+        android:alpha=".1"
         />
     <item
         android:state_activated="true"
diff --git a/samples/Support7Demos/src/main/res/layout/selection_demo_layout.xml b/samples/Support7Demos/src/main/res/layout/selection_demo_layout.xml
index 3fc1f40..27e08bf 100644
--- a/samples/Support7Demos/src/main/res/layout/selection_demo_layout.xml
+++ b/samples/Support7Demos/src/main/res/layout/selection_demo_layout.xml
@@ -55,7 +55,9 @@
                 android:paddingEnd="0dp"
                 android:paddingStart="0dp"
                 android:paddingTop="5dp"
-                android:scrollbars="none" />
+                android:background="#11000000"
+                android:scrollbarStyle="insideOverlay"
+                android:scrollbars="vertical" />
 
         </FrameLayout>
 
diff --git a/samples/Support7Demos/src/main/res/layout/selection_demo_list_item.xml b/samples/Support7Demos/src/main/res/layout/selection_demo_list_item.xml
index fb5e8e9..e28e922 100644
--- a/samples/Support7Demos/src/main/res/layout/selection_demo_list_item.xml
+++ b/samples/Support7Demos/src/main/res/layout/selection_demo_list_item.xml
@@ -40,8 +40,8 @@
       </TextView>
       <TextView
           android:id="@+id/label"
-          android:textSize="20sp"
-          android:textStyle="bold"
+          android:textSize="18sp"
+          android:textColor="@android:color/black"
           android:gravity="center_vertical"
           android:paddingStart="10dp"
           android:paddingEnd="10dp"
diff --git a/samples/Support7Demos/src/main/res/menu/selection_demo_actions.xml b/samples/Support7Demos/src/main/res/menu/selection_demo_actions.xml
index 17ea335..7751f3e 100644
--- a/samples/Support7Demos/src/main/res/menu/selection_demo_actions.xml
+++ b/samples/Support7Demos/src/main/res/menu/selection_demo_actions.xml
@@ -16,13 +16,15 @@
 
 <menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
-       android:id="@+id/option_menu_swipe_during_select"
-       android:title="Swipe When Selection Is Active"
-       android:checkable="true"
-       android:checked="true"/>
+       android:id="@+id/option_menu_more_cheese"
+       android:title="Show all the cheeses!"
+       android:checkable="true"/>
    <item
-       android:id="@+id/option_menu_custom_listener"
-       android:title="Custom OnItemTouchListener"
-       android:checkable="true"
-       android:checked="false"/>
+       android:id="@+id/option_menu_grid_layout"
+       android:title="Grid layout please!"
+       android:checkable="true"/>
+   <item
+       android:id="@+id/option_menu_swipe_during_select"
+       android:title="Swipe when stuff is selected!"
+       android:checkable="true"/>
 </menu>
diff --git a/sqlite/sqlite-inspection/src/androidTest/java/androidx/sqlite/inspection/test/InvalidationTest.kt b/sqlite/sqlite-inspection/src/androidTest/java/androidx/sqlite/inspection/test/InvalidationTest.kt
index 9b9ea21..e3f3171 100644
--- a/sqlite/sqlite-inspection/src/androidTest/java/androidx/sqlite/inspection/test/InvalidationTest.kt
+++ b/sqlite/sqlite-inspection/src/androidTest/java/androidx/sqlite/inspection/test/InvalidationTest.kt
@@ -23,7 +23,6 @@
 import android.database.sqlite.SQLiteDatabase
 import android.database.sqlite.SQLiteStatement
 import androidx.inspection.ArtToolInterface
-import androidx.inspection.InspectorEnvironment
 import androidx.sqlite.inspection.SqliteInspectorProtocol.DatabasePossiblyChangedEvent
 import androidx.sqlite.inspection.SqliteInspectorProtocol.Event.OneOfCase.DATABASE_POSSIBLY_CHANGED
 import androidx.test.core.app.ApplicationProvider
@@ -202,7 +201,7 @@
         this.first { it.originMethod == m && it is Hook.EntryHook }.asEntryHook
 
     @Suppress("UNCHECKED_CAST")
-    private fun List<Hook>.exitHookFor(m: String): InspectorEnvironment.ExitHook<Any> =
+    private fun List<Hook>.exitHookFor(m: String): ArtToolInterface.ExitHook<Any> =
         this.first { it.originMethod == m && it is Hook.ExitHook }
-            .asExitHook as InspectorEnvironment.ExitHook<Any>
+            .asExitHook as ArtToolInterface.ExitHook<Any>
 }
diff --git a/sqlite/sqlite-inspection/src/androidTest/java/androidx/sqlite/inspection/test/TrackDatabasesTest.kt b/sqlite/sqlite-inspection/src/androidTest/java/androidx/sqlite/inspection/test/TrackDatabasesTest.kt
index 389a221..c5f40b9 100644
--- a/sqlite/sqlite-inspection/src/androidTest/java/androidx/sqlite/inspection/test/TrackDatabasesTest.kt
+++ b/sqlite/sqlite-inspection/src/androidTest/java/androidx/sqlite/inspection/test/TrackDatabasesTest.kt
@@ -20,7 +20,7 @@
 import android.database.sqlite.SQLiteClosable
 import android.database.sqlite.SQLiteDatabase
 import android.os.Build
-import androidx.inspection.InspectorEnvironment.ExitHook
+import androidx.inspection.ArtToolInterface.ExitHook
 import androidx.sqlite.inspection.SqliteInspectorProtocol.Event
 import androidx.sqlite.inspection.SqliteInspectorProtocol.Response
 import androidx.sqlite.inspection.test.MessageFactory.createKeepDatabasesOpenCommand
diff --git a/sqlite/sqlite-inspection/src/main/java/androidx/sqlite/inspection/EntryExitMatchingHookRegistry.java b/sqlite/sqlite-inspection/src/main/java/androidx/sqlite/inspection/EntryExitMatchingHookRegistry.java
index 0ee785f..58db50f 100644
--- a/sqlite/sqlite-inspection/src/main/java/androidx/sqlite/inspection/EntryExitMatchingHookRegistry.java
+++ b/sqlite/sqlite-inspection/src/main/java/androidx/sqlite/inspection/EntryExitMatchingHookRegistry.java
@@ -20,6 +20,8 @@
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.inspection.ArtToolInterface.EntryHook;
+import androidx.inspection.ArtToolInterface.ExitHook;
 import androidx.inspection.InspectorEnvironment;
 
 import java.util.ArrayDeque;
@@ -54,8 +56,8 @@
 
     void registerHook(Class<?> originClass, final String originMethod,
             final OnExitCallback onExitCallback) {
-        mEnvironment.registerEntryHook(originClass, originMethod,
-                new InspectorEnvironment.EntryHook() {
+        mEnvironment.artTI().registerEntryHook(originClass, originMethod,
+                new EntryHook() {
                     @SuppressLint("SyntheticAccessor")
                     @Override
                     public void onEntry(@Nullable Object thisObject,
@@ -64,8 +66,8 @@
                     }
                 });
 
-        mEnvironment.registerExitHook(originClass, originMethod,
-                new InspectorEnvironment.ExitHook<Object>() {
+        mEnvironment.artTI().registerExitHook(originClass, originMethod,
+                new ExitHook<Object>() {
                     @SuppressLint("SyntheticAccessor")
                     @Override
                     public Object onExit(Object result) {
diff --git a/sqlite/sqlite-inspection/src/main/java/androidx/sqlite/inspection/RoomInvalidationRegistry.java b/sqlite/sqlite-inspection/src/main/java/androidx/sqlite/inspection/RoomInvalidationRegistry.java
index 08cf69e..0631878 100644
--- a/sqlite/sqlite-inspection/src/main/java/androidx/sqlite/inspection/RoomInvalidationRegistry.java
+++ b/sqlite/sqlite-inspection/src/main/java/androidx/sqlite/inspection/RoomInvalidationRegistry.java
@@ -92,7 +92,8 @@
         if (mInvoker == null) {
             cached = Collections.emptyList();
         } else {
-            List<?> instances = mEnvironment.findInstances(mInvoker.invalidationTrackerClass);
+            List<?> instances =
+                    mEnvironment.artTI().findInstances(mInvoker.invalidationTrackerClass);
             cached = new ArrayList<>(instances.size());
             for (Object instance : instances) {
                 cached.add(new WeakReference<>(instance));
diff --git a/sqlite/sqlite-inspection/src/main/java/androidx/sqlite/inspection/SqlDelightInvalidation.java b/sqlite/sqlite-inspection/src/main/java/androidx/sqlite/inspection/SqlDelightInvalidation.java
index 9f7ad85..7b6c815 100644
--- a/sqlite/sqlite-inspection/src/main/java/androidx/sqlite/inspection/SqlDelightInvalidation.java
+++ b/sqlite/sqlite-inspection/src/main/java/androidx/sqlite/inspection/SqlDelightInvalidation.java
@@ -55,7 +55,7 @@
             return;
         }
         // invalidating all queries because we can't say which ones were actually affected.
-        List<?> queries = mEnvironment.findInstances(mQueryClass);
+        List<?> queries = mEnvironment.artTI().findInstances(mQueryClass);
         for (Object query: queries) {
             notifyDataChanged(query);
         }
diff --git a/sqlite/sqlite-inspection/src/main/java/androidx/sqlite/inspection/SqliteInspector.java b/sqlite/sqlite-inspection/src/main/java/androidx/sqlite/inspection/SqliteInspector.java
index 13c0bc3..16195d9 100644
--- a/sqlite/sqlite-inspection/src/main/java/androidx/sqlite/inspection/SqliteInspector.java
+++ b/sqlite/sqlite-inspection/src/main/java/androidx/sqlite/inspection/SqliteInspector.java
@@ -44,6 +44,8 @@
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.inspection.ArtToolInterface.EntryHook;
+import androidx.inspection.ArtToolInterface.ExitHook;
 import androidx.inspection.Connection;
 import androidx.inspection.Inspector;
 import androidx.inspection.InspectorEnvironment;
@@ -252,7 +254,7 @@
         registerDatabaseClosedHooks(hookRegistry);
 
         // Check for database instances in memory
-        for (SQLiteDatabase instance : mEnvironment.findInstances(SQLiteDatabase.class)) {
+        for (SQLiteDatabase instance : mEnvironment.artTI().findInstances(SQLiteDatabase.class)) {
             /** the race condition here will be handled by mDatabaseRegistry */
             if (instance.isOpen()) {
                 onDatabaseOpened(instance);
@@ -262,7 +264,7 @@
         }
 
         // Check for database instances on disk
-        for (Application instance : mEnvironment.findInstances(Application.class)) {
+        for (Application instance : mEnvironment.artTI().findInstances(Application.class)) {
             for (String name : instance.databaseList()) {
                 File path = instance.getDatabasePath(name);
                 if (path.exists() && !isHelperSqliteFile(path)) {
@@ -296,8 +298,8 @@
                         OPEN_DATABASE_COMMAND_SIGNATURE_API_11,
                         CREATE_IN_MEMORY_DATABASE_COMMAND_SIGNATURE_API_27);
 
-        InspectorEnvironment.ExitHook<SQLiteDatabase> hook =
-                new InspectorEnvironment.ExitHook<SQLiteDatabase>() {
+        ExitHook<SQLiteDatabase> hook =
+                new ExitHook<SQLiteDatabase>() {
                     @SuppressLint("SyntheticAccessor")
                     @Override
                     public SQLiteDatabase onExit(SQLiteDatabase database) {
@@ -316,15 +318,15 @@
                     }
                 };
         for (String method : methods) {
-            mEnvironment.registerExitHook(SQLiteDatabase.class, method, hook);
+            mEnvironment.artTI().registerExitHook(SQLiteDatabase.class, method, hook);
         }
     }
 
     private void registerReleaseReferenceHooks() {
-        mEnvironment.registerEntryHook(
+        mEnvironment.artTI().registerEntryHook(
                 SQLiteClosable.class,
                 "releaseReference()V",
-                new InspectorEnvironment.EntryHook() {
+                new EntryHook() {
                     @Override
                     public void onEntry(@Nullable Object thisObject,
                             @NonNull List<Object> args) {
@@ -374,8 +376,8 @@
      * {@link SQLiteDatabase#setTransactionSuccessful} was called
      */
     private void registerInvalidationHooksTransaction(final RequestCollapsingThrottler throttler) {
-        mEnvironment.registerExitHook(SQLiteDatabase.class, "endTransaction()V",
-                new InspectorEnvironment.ExitHook<Object>() {
+        mEnvironment.artTI().registerExitHook(SQLiteDatabase.class, "endTransaction()V",
+                new ExitHook<Object>() {
                     @Override
                     public Object onExit(Object result) {
                         throttler.submitRequest();
@@ -395,8 +397,8 @@
     private void registerInvalidationHooksSqliteStatement(
             final RequestCollapsingThrottler throttler) {
         for (String method : SQLITE_STATEMENT_EXECUTE_METHODS_SIGNATURES) {
-            mEnvironment.registerExitHook(SQLiteStatement.class, method,
-                    new InspectorEnvironment.ExitHook<Object>() {
+            mEnvironment.artTI().registerExitHook(SQLiteStatement.class, method,
+                    new ExitHook<Object>() {
                         @Override
                         public Object onExit(Object result) {
                             throttler.submitRequest();
diff --git a/ui/settings.gradle b/ui/settings.gradle
index 57520eb..5aade6c 100644
--- a/ui/settings.gradle
+++ b/ui/settings.gradle
@@ -46,10 +46,10 @@
 includeProject(":compose:compose-compiler", "../compose/compose-compiler")
 includeProject(":compose:compose-compiler-hosted", "../compose/compose-compiler-hosted")
 includeProject(":compose:compose-compiler-hosted:integration-tests", "../compose/compose-compiler-hosted/integration-tests")
-includeProject(":compose:runtime:runtime-dispatch", "../compose/compose-dispatch")
-includeProject(":compose:runtime:runtime", "../compose/compose-runtime")
-includeProject(":compose:runtime:runtime:benchmark", "../compose/compose-runtime/compose-runtime-benchmark")
-includeProject(":compose:runtime:runtime:samples", "../compose/compose-runtime/samples")
+includeProject(":compose:runtime:runtime-dispatch", "../compose/runtime/runtime-dispatch")
+includeProject(":compose:runtime:runtime", "../compose/runtime/runtime")
+includeProject(":compose:runtime:runtime:benchmark", "../compose/runtime/runtime/compose-runtime-benchmark")
+includeProject(":compose:runtime:runtime:samples", "../compose/runtime/runtime/samples")
 includeProject(":compose:runtime", "../compose/runtime")
 includeProject(":lint-checks", "../lint-checks")
 includeProject(":tracing", "../tracing")
@@ -65,11 +65,11 @@
 includeProject(":compose:android-view:android-view:integration-tests:android-view-demos", "ui-android-view/integration-tests/android-view-demos")
 includeProject(":compose:android-view:android-view:samples", "ui-android-view/samples")
 includeProject(":compose:animation", "../compose/animation")
-includeProject(":compose:animation:animation", "ui-animation")
-includeProject(":compose:animation:animation-core", "ui-animation-core")
-includeProject(":compose:animation:animation-core:samples", "ui-animation-core/samples")
-includeProject(":compose:animation:animation:integration-tests:animation-demos", "ui-animation/integration-tests/animation-demos")
-includeProject(":compose:animation:animation:samples", "ui-animation/samples")
+includeProject(":compose:animation:animation", "../compose/animation/animation")
+includeProject(":compose:animation:animation-core", "../compose/animation/animation-core")
+includeProject(":compose:animation:animation-core:samples", "../compose/animation/animation-core/samples")
+includeProject(":compose:animation:animation:integration-tests:animation-demos", "../compose/animation/animation/integration-tests/animation-demos")
+includeProject(":compose:animation:animation:samples", "../compose/animation/animation/samples")
 includeProject(":ui", "ui")
 includeProject(":ui:ui-animation-tooling-internal", "ui-animation-tooling-internal")
 includeProject(":compose:ui:ui", "ui-core")
@@ -113,13 +113,13 @@
 includeProject(":compose:ui:ui-unit", "ui-unit")
 includeProject(":compose:ui:ui-unit:samples", "ui-unit/samples")
 includeProject(":compose:ui:ui-util", "ui-util")
-includeProject(":compose:runtime:runtime-saved-instance-state", "ui-saved-instance-state")
-includeProject(":compose:runtime:runtime-saved-instance-state:samples", "ui-saved-instance-state/samples")
-includeProject(":compose:runtime:runtime-livedata", "ui-livedata")
-includeProject(":compose:runtime:runtime-livedata:samples", "ui-livedata/samples")
+includeProject(":compose:runtime:runtime-saved-instance-state", "../compose/runtime/runtime-saved-instance-state")
+includeProject(":compose:runtime:runtime-saved-instance-state:samples", "../compose/runtime/runtime-saved-instance-state/samples")
+includeProject(":compose:runtime:runtime-livedata", "../compose/runtime/runtime-livedata")
+includeProject(":compose:runtime:runtime-livedata:samples", "../compose/runtime/runtime-livedata/samples")
 includeProject(":test-screenshot", "../test/screenshot")
-includeProject(":compose:runtime:runtime-rxjava2", "ui-rxjava2")
-includeProject(":compose:runtime:runtime-rxjava2:samples", "ui-rxjava2/samples")
+includeProject(":compose:runtime:runtime-rxjava2", "../compose/runtime/runtime-rxjava2")
+includeProject(":compose:runtime:runtime-rxjava2:samples", "../compose/runtime/runtime-rxjava2/samples")
 includeProject(":compose:test-utils", "../compose/test-utils")
 includeProject(":compose:ui:ui-viewbinding", "ui-core-viewbinding")
 includeProject(":compose:ui:ui-viewbinding:samples", "ui-core-viewbinding/samples")
diff --git a/ui/ui-animation/api/res-0.1.0-dev04.txt b/ui/ui-animation/api/res-0.1.0-dev04.txt
deleted file mode 100644
index e69de29..0000000
--- a/ui/ui-animation/api/res-0.1.0-dev04.txt
+++ /dev/null
diff --git a/ui/ui-animation/api/res-0.1.0-dev05.txt b/ui/ui-animation/api/res-0.1.0-dev05.txt
deleted file mode 100644
index e69de29..0000000
--- a/ui/ui-animation/api/res-0.1.0-dev05.txt
+++ /dev/null
diff --git a/ui/ui-animation/api/res-0.1.0-dev06.txt b/ui/ui-animation/api/res-0.1.0-dev06.txt
deleted file mode 100644
index e69de29..0000000
--- a/ui/ui-animation/api/res-0.1.0-dev06.txt
+++ /dev/null
diff --git a/ui/ui-animation/api/res-0.1.0-dev07.txt b/ui/ui-animation/api/res-0.1.0-dev07.txt
deleted file mode 100644
index e69de29..0000000
--- a/ui/ui-animation/api/res-0.1.0-dev07.txt
+++ /dev/null
diff --git a/ui/ui-animation/api/res-0.1.0-dev08.txt b/ui/ui-animation/api/res-0.1.0-dev08.txt
deleted file mode 100644
index e69de29..0000000
--- a/ui/ui-animation/api/res-0.1.0-dev08.txt
+++ /dev/null
diff --git a/ui/ui-animation/api/res-0.1.0-dev09.txt b/ui/ui-animation/api/res-0.1.0-dev09.txt
deleted file mode 100644
index e69de29..0000000
--- a/ui/ui-animation/api/res-0.1.0-dev09.txt
+++ /dev/null
diff --git a/ui/ui-animation/api/res-0.1.0-dev10.txt b/ui/ui-animation/api/res-0.1.0-dev10.txt
deleted file mode 100644
index e69de29..0000000
--- a/ui/ui-animation/api/res-0.1.0-dev10.txt
+++ /dev/null
diff --git a/ui/ui-animation/api/res-0.1.0-dev11.txt b/ui/ui-animation/api/res-0.1.0-dev11.txt
deleted file mode 100644
index e69de29..0000000
--- a/ui/ui-animation/api/res-0.1.0-dev11.txt
+++ /dev/null
diff --git a/ui/ui-animation/api/res-0.1.0-dev12.txt b/ui/ui-animation/api/res-0.1.0-dev12.txt
deleted file mode 100644
index e69de29..0000000
--- a/ui/ui-animation/api/res-0.1.0-dev12.txt
+++ /dev/null
diff --git a/ui/ui-animation/api/res-0.1.0-dev14.txt b/ui/ui-animation/api/res-0.1.0-dev14.txt
deleted file mode 100644
index e69de29..0000000
--- a/ui/ui-animation/api/res-0.1.0-dev14.txt
+++ /dev/null
diff --git a/ui/ui-animation/api/res-0.1.0-dev15.txt b/ui/ui-animation/api/res-0.1.0-dev15.txt
deleted file mode 100644
index e69de29..0000000
--- a/ui/ui-animation/api/res-0.1.0-dev15.txt
+++ /dev/null
diff --git a/ui/ui-animation/api/res-0.1.0-dev16.txt b/ui/ui-animation/api/res-0.1.0-dev16.txt
deleted file mode 100644
index e69de29..0000000
--- a/ui/ui-animation/api/res-0.1.0-dev16.txt
+++ /dev/null
diff --git a/ui/ui-animation/api/res-current.txt b/ui/ui-animation/api/res-current.txt
deleted file mode 100644
index e69de29..0000000
--- a/ui/ui-animation/api/res-current.txt
+++ /dev/null
diff --git a/ui/ui-core/build.gradle b/ui/ui-core/build.gradle
index aff8277..b49e2e0 100644
--- a/ui/ui-core/build.gradle
+++ b/ui/ui-core/build.gradle
@@ -113,6 +113,16 @@
             implementation project(":compose:foundation:foundation-layout")
             implementation project(":compose:foundation:foundation")
         }
+
+        desktopTest.dependencies {
+            implementation(TRUTH)
+            implementation(JUNIT)
+            implementation(MOCKITO_CORE)
+            implementation MOCKITO_KOTLIN, {
+                exclude group: 'org.mockito' // to keep control on the mockito version
+            }
+            implementation(SKIKO_CURRENT_OS)
+        }
     }
 }
 
diff --git a/ui/ui-core/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopOwner.kt b/ui/ui-core/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopOwner.kt
index fc5b7d8..c20e8d8 100644
--- a/ui/ui-core/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopOwner.kt
+++ b/ui/ui-core/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopOwner.kt
@@ -166,16 +166,23 @@
         container.invalidate()
     }
 
+    // Don't inline these variables into snapshotObserver.observeReads,
+    // because observeReads requires that onChanged should always be the same instance.
+    // Otherwise there will be a memory leak and FPS drop (see b/163905871)
+    private val onCommitAffectingLayout = ::onRequestRelayout
+    private val onCommitAffectingMeasure = ::onRequestMeasure
+    private val onCommitAffectingLayer = OwnedLayer::invalidate
+
     override fun observeLayoutModelReads(node: LayoutNode, block: () -> Unit) {
-        snapshotObserver.observeReads(node, ::onRequestRelayout, block)
+        snapshotObserver.observeReads(node, onCommitAffectingLayout, block)
     }
 
     override fun observeMeasureModelReads(node: LayoutNode, block: () -> Unit) {
-        snapshotObserver.observeReads(node, ::onRequestMeasure, block)
+        snapshotObserver.observeReads(node, onCommitAffectingMeasure, block)
     }
 
     private fun observeDrawModelReads(layer: SkijaLayer, block: () -> Unit) {
-        snapshotObserver.observeReads(layer, OwnedLayer::invalidate, block)
+        snapshotObserver.observeReads(layer, onCommitAffectingLayer, block)
     }
 
     override fun createLayer(
diff --git a/ui/ui-core/src/desktopTest/kotlin/androidx/compose/ui/platform/DesktopOwnerTest.kt b/ui/ui-core/src/desktopTest/kotlin/androidx/compose/ui/platform/DesktopOwnerTest.kt
new file mode 100644
index 0000000..b571ab0
--- /dev/null
+++ b/ui/ui-core/src/desktopTest/kotlin/androidx/compose/ui/platform/DesktopOwnerTest.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2020 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.ui.platform
+
+import androidx.compose.runtime.ExperimentalComposeApi
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.snapshots.Snapshot
+import androidx.compose.ui.node.ExperimentalLayoutNodeApi
+import androidx.compose.ui.node.LayoutNode
+import com.nhaarman.mockitokotlin2.doAnswer
+import com.nhaarman.mockitokotlin2.doReturn
+import com.nhaarman.mockitokotlin2.mock
+import org.jetbrains.skiko.Library
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+@OptIn(ExperimentalLayoutNodeApi::class, ExperimentalComposeApi::class)
+class DesktopOwnerTest {
+    @Test
+    fun `single invalidate with multiple observers and single state change`() {
+        Library.load("/", "skiko")
+
+        var invalidateCount = 0
+
+        val owners = mock<DesktopOwners> {
+            on { platformInputService } doReturn mock()
+            on { invalidate() }.doAnswer { invalidateCount++; Unit }
+        }
+        val owner = DesktopOwner(owners)
+        val node = LayoutNode()
+        val state = mutableStateOf(2)
+
+        owner.observeLayoutModelReads(node) {
+            state.value
+        }
+
+        owner.observeLayoutModelReads(node) {
+            state.value
+        }
+
+        val oldInvalidateCount = invalidateCount
+
+        Snapshot.notifyObjectsInitialized()
+        state.value++
+        Snapshot.sendApplyNotifications()
+
+        assertEquals(1, invalidateCount - oldInvalidateCount)
+    }
+}
\ No newline at end of file
diff --git a/ui/ui-core/src/desktopTest/resources/mockito-extensions/org.mockito.plugins.MockMaker b/ui/ui-core/src/desktopTest/resources/mockito-extensions/org.mockito.plugins.MockMaker
new file mode 100644
index 0000000..ca6ee9c
--- /dev/null
+++ b/ui/ui-core/src/desktopTest/resources/mockito-extensions/org.mockito.plugins.MockMaker
@@ -0,0 +1 @@
+mock-maker-inline
\ No newline at end of file
diff --git a/ui/ui-desktop/build.gradle b/ui/ui-desktop/build.gradle
index efea5fd..d99bc31 100644
--- a/ui/ui-desktop/build.gradle
+++ b/ui/ui-desktop/build.gradle
@@ -60,9 +60,7 @@
             resources.srcDirs += new File(SupportConfigKt.getExternalProjectPath(project), "noto-fonts/other/")
             resources.srcDirs += "src/jvmTest/res"
             dependencies {
-                implementation(SKIKO_WINDOWS)
-                implementation(SKIKO_LINUX)
-                implementation(SKIKO_MACOS)
+                implementation(SKIKO_CURRENT_OS)
                 implementation(TRUTH)
             }
         }
diff --git a/ui/ui-desktop/samples/build.gradle b/ui/ui-desktop/samples/build.gradle
index 9320e0c..c11ed39 100644
--- a/ui/ui-desktop/samples/build.gradle
+++ b/ui/ui-desktop/samples/build.gradle
@@ -40,9 +40,7 @@
         }
 
         jvmMain.dependencies {
-            implementation(SKIKO_WINDOWS)
-            implementation(SKIKO_LINUX)
-            implementation(SKIKO_MACOS)
+            implementation(SKIKO_CURRENT_OS)
             implementation project(":compose:desktop:desktop")
         }
     }
diff --git a/ui/ui-foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/TextFieldCursorTest.kt b/ui/ui-foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/TextFieldCursorTest.kt
index d10d774..b073db6 100644
--- a/ui/ui-foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/TextFieldCursorTest.kt
+++ b/ui/ui-foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/TextFieldCursorTest.kt
@@ -46,7 +46,6 @@
 import org.junit.Test
 import java.util.concurrent.CountDownLatch
 import java.util.concurrent.TimeUnit
-import kotlin.math.roundToInt
 
 @LargeTest
 @OptIn(
@@ -142,19 +141,19 @@
     }
 
     private fun Bitmap.assertCursor(cursorWidth: Dp, density: Density) {
-        val halfCursorWidth = (with(density) { cursorWidth.toIntPx() } / 2f).roundToInt()
+        val сursorWidth = (with(density) { cursorWidth.toIntPx() })
         val width = width
         val height = height
         this.assertPixels(
             IntSize(width, height)
         ) { position ->
-            if (position.x >= halfCursorWidth - 1 && position.x < halfCursorWidth + 1) {
+            if (position.x >= сursorWidth - 1 && position.x < сursorWidth + 1) {
                 // skip some pixels around cursor
                 null
             } else if (position.y < 5 || position.y > height - 5) {
                 // skip some pixels vertically
                 null
-            } else if (position.x in 0..halfCursorWidth) {
+            } else if (position.x in 0..сursorWidth) {
                 // cursor
                 Color.Red
             } else {
diff --git a/ui/ui-foundation/src/commonMain/kotlin/androidx/compose/foundation/BaseTextField.kt b/ui/ui-foundation/src/commonMain/kotlin/androidx/compose/foundation/BaseTextField.kt
index b78fc90..0a15d0c 100644
--- a/ui/ui-foundation/src/commonMain/kotlin/androidx/compose/foundation/BaseTextField.kt
+++ b/ui/ui-foundation/src/commonMain/kotlin/androidx/compose/foundation/BaseTextField.kt
@@ -237,7 +237,8 @@
                 0f, 0f,
                 cursorWidth, cursorHeight
             )
-            val cursorX = (cursorRect.left + cursorRect.right) / 2
+            val cursorX = (cursorRect.left + cursorWidth / 2)
+                .coerceAtMost(size.width - cursorWidth / 2)
 
             drawLine(
                 color.value,
diff --git a/ui/ui-text-core/src/androidAndroidTest/kotlin/androidx/compose/ui/text/MultiParagraphIntegrationTest.kt b/ui/ui-text-core/src/androidAndroidTest/kotlin/androidx/compose/ui/text/MultiParagraphIntegrationTest.kt
index 8459fd4..32b1e38 100644
--- a/ui/ui-text-core/src/androidAndroidTest/kotlin/androidx/compose/ui/text/MultiParagraphIntegrationTest.kt
+++ b/ui/ui-text-core/src/androidAndroidTest/kotlin/androidx/compose/ui/text/MultiParagraphIntegrationTest.kt
@@ -530,9 +530,9 @@
                 val cursorXOffset = col * fontSizeInPx
 
                 val expectRect = Rect(
-                    left = cursorXOffset - cursorWidth / 2,
+                    left = cursorXOffset,
                     top = top,
-                    right = cursorXOffset + cursorWidth / 2,
+                    right = cursorXOffset,
                     bottom = top + fontSizeInPx
                 )
                 val actualRect = paragraph.getCursorRect(i)
@@ -548,9 +548,9 @@
             val cursorXOffset = col * fontSizeInPx
 
             val expectRect = Rect(
-                left = cursorXOffset - cursorWidth / 2,
+                left = cursorXOffset,
                 top = top,
-                right = cursorXOffset + cursorWidth / 2,
+                right = cursorXOffset,
                 bottom = top + fontSizeInPx
             )
             val actualRect = paragraph.getCursorRect(text.length)
diff --git a/ui/ui-text-core/src/androidAndroidTest/kotlin/androidx/compose/ui/text/ParagraphIntegrationTest.kt b/ui/ui-text-core/src/androidAndroidTest/kotlin/androidx/compose/ui/text/ParagraphIntegrationTest.kt
index 6c61b7e..e8fbb6b 100644
--- a/ui/ui-text-core/src/androidAndroidTest/kotlin/androidx/compose/ui/text/ParagraphIntegrationTest.kt
+++ b/ui/ui-text-core/src/androidAndroidTest/kotlin/androidx/compose/ui/text/ParagraphIntegrationTest.kt
@@ -65,8 +65,6 @@
 
     private val resourceLoader = TestFontResourceLoader(context)
 
-    private val cursorWidth = 4f
-
     @Test
     fun empty_string() {
         with(defaultDensity) {
@@ -588,9 +586,9 @@
                 val cursorXOffset = i * fontSizeInPx
                 assertThat(cursorRect).isEqualTo(
                     Rect(
-                        left = cursorXOffset - cursorWidth / 2,
+                        left = cursorXOffset,
                         top = 0f,
-                        right = cursorXOffset + cursorWidth / 2,
+                        right = cursorXOffset,
                         bottom = fontSizeInPx
                     )
                 )
@@ -615,9 +613,9 @@
                 val cursorXOffset = i * fontSizeInPx
                 assertThat(paragraph.getCursorRect(i)).isEqualTo(
                     Rect(
-                        left = cursorXOffset - cursorWidth / 2,
+                        left = cursorXOffset,
                         top = 0f,
-                        right = cursorXOffset + cursorWidth / 2,
+                        right = cursorXOffset,
                         bottom = fontSizeInPx
                     )
                 )
@@ -627,9 +625,9 @@
                 val cursorXOffset = (i % charsPerLine) * fontSizeInPx
                 assertThat(paragraph.getCursorRect(i)).isEqualTo(
                     Rect(
-                        left = cursorXOffset - cursorWidth / 2,
+                        left = cursorXOffset,
                         top = fontSizeInPx,
-                        right = cursorXOffset + cursorWidth / 2,
+                        right = cursorXOffset,
                         bottom = fontSizeInPx * 2.2f
                     )
                 )
@@ -651,9 +649,9 @@
             // Cursor before '\n'
             assertThat(paragraph.getCursorRect(3)).isEqualTo(
                 Rect(
-                    left = 3 * fontSizeInPx - cursorWidth / 2,
+                    left = 3 * fontSizeInPx,
                     top = 0f,
-                    right = 3 * fontSizeInPx + cursorWidth / 2,
+                    right = 3 * fontSizeInPx,
                     bottom = fontSizeInPx
                 )
             )
@@ -661,9 +659,9 @@
             // Cursor after '\n'
             assertThat(paragraph.getCursorRect(4)).isEqualTo(
                 Rect(
-                    left = -cursorWidth / 2,
+                    left = 0f,
                     top = fontSizeInPx,
-                    right = cursorWidth / 2,
+                    right = 0f,
                     bottom = fontSizeInPx * 2.2f
                 )
             )
@@ -684,9 +682,9 @@
             // Cursor before '\n'
             assertThat(paragraph.getCursorRect(3)).isEqualTo(
                 Rect(
-                    left = 3 * fontSizeInPx - cursorWidth / 2,
+                    left = 3 * fontSizeInPx,
                     top = 0f,
-                    right = 3 * fontSizeInPx + cursorWidth / 2,
+                    right = 3 * fontSizeInPx,
                     bottom = fontSizeInPx
                 )
             )
@@ -694,9 +692,9 @@
             // Cursor after '\n'
             assertThat(paragraph.getCursorRect(4)).isEqualTo(
                 Rect(
-                    left = -cursorWidth / 2,
+                    left = 0f,
                     top = fontSizeInPx,
-                    right = cursorWidth / 2,
+                    right = 0f,
                     bottom = fontSizeInPx * 2.2f
                 )
             )
@@ -719,9 +717,9 @@
                 val cursorXOffset = (text.length - i) * fontSizeInPx
                 assertThat(paragraph.getCursorRect(i)).isEqualTo(
                     Rect(
-                        left = cursorXOffset - cursorWidth / 2,
+                        left = cursorXOffset,
                         top = 0f,
-                        right = cursorXOffset + cursorWidth / 2,
+                        right = cursorXOffset,
                         bottom = fontSizeInPx
                     )
                 )
@@ -746,9 +744,9 @@
                 val cursorXOffset = (charsPerLine - i) * fontSizeInPx
                 assertThat(paragraph.getCursorRect(i)).isEqualTo(
                     Rect(
-                        left = cursorXOffset - cursorWidth / 2,
+                        left = cursorXOffset,
                         top = 0f,
-                        right = cursorXOffset + cursorWidth / 2,
+                        right = cursorXOffset,
                         bottom = fontSizeInPx
                     )
                 )
@@ -758,9 +756,9 @@
                 val cursorXOffset = (charsPerLine - i % charsPerLine) * fontSizeInPx
                 assertThat(paragraph.getCursorRect(i)).isEqualTo(
                     Rect(
-                        left = cursorXOffset - cursorWidth / 2,
+                        left = cursorXOffset,
                         top = fontSizeInPx,
-                        right = cursorXOffset + cursorWidth / 2,
+                        right = cursorXOffset,
                         bottom = fontSizeInPx * 2.2f
                     )
                 )
@@ -783,9 +781,9 @@
             // Cursor before '\n'
             assertThat(paragraph.getCursorRect(3)).isEqualTo(
                 Rect(
-                    left = 0 - cursorWidth / 2,
+                    left = 0f,
                     top = 0f,
-                    right = 0 + cursorWidth / 2,
+                    right = 0f,
                     bottom = fontSizeInPx
                 )
             )
@@ -793,9 +791,9 @@
             // Cursor after '\n'
             assertThat(paragraph.getCursorRect(4)).isEqualTo(
                 Rect(
-                    left = 3 * fontSizeInPx - cursorWidth / 2,
+                    left = 3 * fontSizeInPx,
                     top = fontSizeInPx,
-                    right = 3 * fontSizeInPx + cursorWidth / 2,
+                    right = 3 * fontSizeInPx,
                     bottom = fontSizeInPx * 2.2f
                 )
             )
@@ -818,9 +816,9 @@
             // Cursor before '\n'
             assertThat(paragraph.getCursorRect(3)).isEqualTo(
                 Rect(
-                    left = 0 - cursorWidth / 2,
+                    left = 0f,
                     top = 0f,
-                    right = 0 + cursorWidth / 2,
+                    right = 0f,
                     bottom = fontSizeInPx
                 )
             )
@@ -828,9 +826,9 @@
             // Cursor after '\n'
             assertThat(paragraph.getCursorRect(4)).isEqualTo(
                 Rect(
-                    left = -cursorWidth / 2,
+                    left = 0f,
                     top = fontSizeInPx,
-                    right = +cursorWidth / 2,
+                    right = 0f,
                     bottom = fontSizeInPx * 2.2f
                 )
             )
diff --git a/ui/ui-text-core/src/androidMain/kotlin/androidx/compose/ui/text/platform/AndroidParagraph.kt b/ui/ui-text-core/src/androidMain/kotlin/androidx/compose/ui/text/platform/AndroidParagraph.kt
index 8665c44..abff882 100644
--- a/ui/ui-text-core/src/androidMain/kotlin/androidx/compose/ui/text/platform/AndroidParagraph.kt
+++ b/ui/ui-text-core/src/androidMain/kotlin/androidx/compose/ui/text/platform/AndroidParagraph.kt
@@ -256,14 +256,15 @@
         if (offset !in 0..charSequence.length) {
             throw AssertionError("offset($offset) is out of bounds (0,${charSequence.length}")
         }
-        val cursorWidth = 4.0f
         val horizontal = layout.getPrimaryHorizontal(offset)
         val line = layout.getLineForOffset(offset)
 
+        // The width of the cursor is not taken into account. The callers of this API should use
+        // rect.left to get the start X position and then adjust it according to the width if needed
         return Rect(
-            horizontal - 0.5f * cursorWidth,
+            horizontal,
             layout.getLineTop(line),
-            horizontal + 0.5f * cursorWidth,
+            horizontal,
             layout.getLineBottom(line)
         )
     }
diff --git a/wear/wear/src/androidTest/java/androidx/wear/widget/drawer/WearableDrawerLayoutEspressoTest.java b/wear/wear/src/androidTest/java/androidx/wear/widget/drawer/WearableDrawerLayoutEspressoTest.java
index 6887f25..9d48837 100644
--- a/wear/wear/src/androidTest/java/androidx/wear/widget/drawer/WearableDrawerLayoutEspressoTest.java
+++ b/wear/wear/src/androidTest/java/androidx/wear/widget/drawer/WearableDrawerLayoutEspressoTest.java
@@ -31,7 +31,9 @@
 import static org.hamcrest.Matchers.allOf;
 import static org.hamcrest.Matchers.is;
 import static org.hamcrest.Matchers.not;
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.mock;
@@ -250,6 +252,49 @@
     }
 
     @Test
+    public void drawerContentViewShouldOnlyInflateOnceOpened() {
+        // GIVEN a launched activity with only an action drawer
+        activityRule.launchActivity(
+                new DrawerTestActivity.Builder()
+                        .setStyle(DrawerStyle.ONLY_ACTION_DRAWER_WITH_TITLE)
+                        .build());
+
+        final RecyclerView actionList =
+                activityRule.getActivity().findViewById(R.id.action_list);
+
+        // WHEN it is opened
+        WearableDrawerView actionDrawer = activityRule.getActivity().findViewById(
+                R.id.action_drawer);
+        openDrawer(actionDrawer);
+
+        // THEN the action drawer should be open and the items should all exist in the actionList
+        // adapter
+        onView(withId(R.id.action_drawer))
+                .perform(
+                        waitForMatchingView(
+                                allOf(withId(R.id.action_drawer), isOpened(true)),
+                                MAX_WAIT_MS));
+        assertEquals(7, actionList.getAdapter().getItemCount());
+    }
+
+    @Test
+    public void drawerContentViewShouldNotInflateAfterLaunch() {
+        // GIVEN a launched activity with only an action drawer
+        activityRule.launchActivity(
+                new DrawerTestActivity.Builder()
+                        .setStyle(DrawerStyle.ONLY_ACTION_DRAWER_WITH_TITLE)
+                        .build());
+
+        final RecyclerView actionList =
+                activityRule.getActivity().findViewById(R.id.action_list);
+
+        // THEN the drawer should not be visible and the draw action list should not have an
+        // adapter set
+        onView(allOf(withId(R.id.action_list), not(isDisplayed())));
+        assertNull(actionList.getAdapter());
+    }
+
+    @Test
     public void navDrawerShouldOpenWhenCalledInOnCreateAndThenCloseWhenRequested() {
         // GIVEN an activity which calls openDrawer(Gravity.TOP) in onCreate, then closes it
         // WHEN it is launched
diff --git a/wear/wear/src/main/java/androidx/wear/widget/drawer/WearableActionDrawerView.java b/wear/wear/src/main/java/androidx/wear/widget/drawer/WearableActionDrawerView.java
index 4d8439e..191d502 100644
--- a/wear/wear/src/main/java/androidx/wear/widget/drawer/WearableActionDrawerView.java
+++ b/wear/wear/src/main/java/androidx/wear/widget/drawer/WearableActionDrawerView.java
@@ -185,14 +185,18 @@
                 .getDimensionPixelOffset(R.dimen.ws_action_drawer_item_icon_right_margin);
 
         mActionList = new RecyclerView(context);
+        mActionList.setId(R.id.action_list);
         mActionList.setLayoutManager(new LinearLayoutManager(context));
         mActionListAdapter = new ActionListAdapter(getMenu());
-        mActionList.setAdapter(mActionListAdapter);
+        // Do not bind the mActionListAdapter to the action list here. We will bind it when/if the
+        // drawer is first opened to avoid the inflation cost in the case that the drawer is never
+        // used
         setDrawerContent(mActionList);
     }
 
     @Override
     public void onDrawerOpened() {
+        setContentIfFirstCall();
         if (mActionListAdapter.getItemCount() > 0) {
             RecyclerView.ViewHolder holder = mActionList.findViewHolderForAdapterPosition(0);
             if (holder != null && holder.itemView != null) {
@@ -201,6 +205,12 @@
         }
     }
 
+    private void setContentIfFirstCall() {
+        if (mActionList.getAdapter() == null) {
+            mActionList.setAdapter(mActionListAdapter);
+        }
+    }
+
     @Override
     public boolean canScrollHorizontally(int direction) {
         // Prevent the window from being swiped closed while it is open by saying that it can scroll
diff --git a/wear/wear/src/main/res/values/ids.xml b/wear/wear/src/main/res/values/ids.xml
index 8a7324e..6341805 100644
--- a/wear/wear/src/main/res/values/ids.xml
+++ b/wear/wear/src/main/res/values/ids.xml
@@ -15,4 +15,5 @@
 -->
 <resources>
     <item name="ws_navigation_drawer_view_pager" type="id" />
+    <item name="action_list" type="id" />
 </resources>
diff --git a/work/workmanager-inspection/src/main/java/androidx/work/inspection/WorkManagerInspector.kt b/work/workmanager-inspection/src/main/java/androidx/work/inspection/WorkManagerInspector.kt
index d74a194..791320c 100644
--- a/work/workmanager-inspection/src/main/java/androidx/work/inspection/WorkManagerInspector.kt
+++ b/work/workmanager-inspection/src/main/java/androidx/work/inspection/WorkManagerInspector.kt
@@ -59,24 +59,23 @@
     private val stackTraceMap = mutableMapOf<String, Array<StackTraceElement>>()
 
     init {
-        workManager = environment.findInstances(Application::class.java).first()
+        workManager = environment.artTI().findInstances(Application::class.java).first()
             .let { application -> WorkManager.getInstance(application) as WorkManagerImpl }
         Handler(Looper.getMainLooper()).post {
             lifecycleRegistry.currentState = Lifecycle.State.STARTED
         }
 
-        environment.registerEntryHook(
+        environment.artTI().registerEntryHook(
             WorkContinuationImpl::class.java,
-            "enqueue()Landroidx/work/Operation;",
-            InspectorEnvironment.EntryHook { obj, _ ->
-                    val stackTrace = Throwable().stackTrace
-                    executor.submit {
-                        (obj as? WorkContinuationImpl)?.allIds?.forEach { id ->
-                            stackTraceMap[id] = stackTrace.prune()
-                        }
-                    }
+            "enqueue()Landroidx/work/Operation;"
+        ) { obj, _ ->
+            val stackTrace = Throwable().stackTrace
+            executor.submit {
+                (obj as? WorkContinuationImpl)?.allIds?.forEach { id ->
+                    stackTraceMap[id] = stackTrace.prune()
+                }
             }
-        )
+        }
     }
 
     override fun onReceiveCommand(data: ByteArray, callback: CommandCallback) {