Merge changes I1366e7fe,I0902eb16,I77fe618a,I6dcac248 into androidx-main

* changes:
  Rename rememberSavedInstanceState() to rememberSaveable()
  Rename RestorableStateHolder to SaveableStateHolder
  Move Saver/listSaver/mapSaver/autoSaver() into a new package
  Rename compose:runtime-saved-instance-state to compose:runtime-saveable
diff --git a/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt b/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
index c558ee6..930cf26 100644
--- a/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
@@ -138,7 +138,7 @@
     val WEAR_WATCHFACE_EDITOR = Version("1.0.0-alpha06")
     val WEAR_WATCHFACE_STYLE = Version("1.0.0-alpha06")
     val WEBKIT = Version("1.5.0-alpha01")
-    val WINDOW = Version("1.0.0-alpha02")
+    val WINDOW = Version("1.0.0-alpha03")
     val WINDOW_EXTENSIONS = Version("1.0.0-alpha01")
     val WINDOW_SIDECAR = Version("0.1.0-alpha01")
     val WORK = Version("2.5.0-rc01")
diff --git a/compose/animation/animation-core/api/current.txt b/compose/animation/animation-core/api/current.txt
index acd2ab7..2dccb46 100644
--- a/compose/animation/animation-core/api/current.txt
+++ b/compose/animation/animation-core/api/current.txt
@@ -81,7 +81,9 @@
     method public T! getValue(long playTime);
     method public V getVelocityVector(long playTime);
     method public default boolean isFinished(long playTime);
+    method public boolean isInfinite();
     property public abstract long durationMillis;
+    property public abstract boolean isInfinite;
     property public abstract T! targetValue;
     property public abstract androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter;
   }
@@ -294,9 +296,11 @@
     method public androidx.compose.animation.core.TwoWayConverter<T,V> getTypeConverter();
     method public T! getValue(long playTime);
     method public V getVelocityVector(long playTime);
+    method public boolean isInfinite();
     property public long durationMillis;
     property public final T! initialValue;
     property public final V initialVelocityVector;
+    property public boolean isInfinite;
     property public T! targetValue;
     property public androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter;
   }
@@ -446,7 +450,7 @@
     property public final boolean hasObservers;
   }
 
-  public final class ManualFrameClock implements androidx.compose.runtime.dispatch.MonotonicFrameClock {
+  public final class ManualFrameClock implements androidx.compose.runtime.MonotonicFrameClock {
     ctor @Deprecated public ManualFrameClock(long initialTime, boolean dispatchOnSubscribe);
     ctor public ManualFrameClock(long initialTime);
     method public void advanceClock(long nanos);
@@ -560,8 +564,10 @@
     method public androidx.compose.animation.core.TwoWayConverter<T,V> getTypeConverter();
     method public T! getValue(long playTime);
     method public V getVelocityVector(long playTime);
+    method public boolean isInfinite();
     property public long durationMillis;
     property public final T! initialValue;
+    property public boolean isInfinite;
     property public T! targetValue;
     property public androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter;
   }
@@ -650,6 +656,8 @@
     method public default V getEndVelocity(V start, V end, V startVelocity);
     method public V getValue(long playTime, V start, V end, V startVelocity);
     method public V getVelocity(long playTime, V start, V end, V startVelocity);
+    method public boolean isInfinite();
+    property public abstract boolean isInfinite;
   }
 
   public final class VectorizedAnimationSpecKt {
@@ -673,6 +681,8 @@
   }
 
   public interface VectorizedFiniteAnimationSpec<V extends androidx.compose.animation.core.AnimationVector> extends androidx.compose.animation.core.VectorizedAnimationSpec<V> {
+    method public default boolean isInfinite();
+    property public default boolean isInfinite;
   }
 
   public final class VectorizedFloatAnimationSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedFiniteAnimationSpec<V> {
@@ -684,6 +694,8 @@
 
   public final class VectorizedInfiniteRepeatableSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedAnimationSpec<V> {
     ctor public VectorizedInfiniteRepeatableSpec(androidx.compose.animation.core.VectorizedDurationBasedAnimationSpec<V> animation, androidx.compose.animation.core.RepeatMode repeatMode);
+    method public boolean isInfinite();
+    property public boolean isInfinite;
   }
 
   public final class VectorizedKeyframesSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedDurationBasedAnimationSpec<V> {
diff --git a/compose/animation/animation-core/api/public_plus_experimental_current.txt b/compose/animation/animation-core/api/public_plus_experimental_current.txt
index acd2ab7..2dccb46 100644
--- a/compose/animation/animation-core/api/public_plus_experimental_current.txt
+++ b/compose/animation/animation-core/api/public_plus_experimental_current.txt
@@ -81,7 +81,9 @@
     method public T! getValue(long playTime);
     method public V getVelocityVector(long playTime);
     method public default boolean isFinished(long playTime);
+    method public boolean isInfinite();
     property public abstract long durationMillis;
+    property public abstract boolean isInfinite;
     property public abstract T! targetValue;
     property public abstract androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter;
   }
@@ -294,9 +296,11 @@
     method public androidx.compose.animation.core.TwoWayConverter<T,V> getTypeConverter();
     method public T! getValue(long playTime);
     method public V getVelocityVector(long playTime);
+    method public boolean isInfinite();
     property public long durationMillis;
     property public final T! initialValue;
     property public final V initialVelocityVector;
+    property public boolean isInfinite;
     property public T! targetValue;
     property public androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter;
   }
@@ -446,7 +450,7 @@
     property public final boolean hasObservers;
   }
 
-  public final class ManualFrameClock implements androidx.compose.runtime.dispatch.MonotonicFrameClock {
+  public final class ManualFrameClock implements androidx.compose.runtime.MonotonicFrameClock {
     ctor @Deprecated public ManualFrameClock(long initialTime, boolean dispatchOnSubscribe);
     ctor public ManualFrameClock(long initialTime);
     method public void advanceClock(long nanos);
@@ -560,8 +564,10 @@
     method public androidx.compose.animation.core.TwoWayConverter<T,V> getTypeConverter();
     method public T! getValue(long playTime);
     method public V getVelocityVector(long playTime);
+    method public boolean isInfinite();
     property public long durationMillis;
     property public final T! initialValue;
+    property public boolean isInfinite;
     property public T! targetValue;
     property public androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter;
   }
@@ -650,6 +656,8 @@
     method public default V getEndVelocity(V start, V end, V startVelocity);
     method public V getValue(long playTime, V start, V end, V startVelocity);
     method public V getVelocity(long playTime, V start, V end, V startVelocity);
+    method public boolean isInfinite();
+    property public abstract boolean isInfinite;
   }
 
   public final class VectorizedAnimationSpecKt {
@@ -673,6 +681,8 @@
   }
 
   public interface VectorizedFiniteAnimationSpec<V extends androidx.compose.animation.core.AnimationVector> extends androidx.compose.animation.core.VectorizedAnimationSpec<V> {
+    method public default boolean isInfinite();
+    property public default boolean isInfinite;
   }
 
   public final class VectorizedFloatAnimationSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedFiniteAnimationSpec<V> {
@@ -684,6 +694,8 @@
 
   public final class VectorizedInfiniteRepeatableSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedAnimationSpec<V> {
     ctor public VectorizedInfiniteRepeatableSpec(androidx.compose.animation.core.VectorizedDurationBasedAnimationSpec<V> animation, androidx.compose.animation.core.RepeatMode repeatMode);
+    method public boolean isInfinite();
+    property public boolean isInfinite;
   }
 
   public final class VectorizedKeyframesSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedDurationBasedAnimationSpec<V> {
diff --git a/compose/animation/animation-core/api/restricted_current.txt b/compose/animation/animation-core/api/restricted_current.txt
index 47a95c9..9cf625e 100644
--- a/compose/animation/animation-core/api/restricted_current.txt
+++ b/compose/animation/animation-core/api/restricted_current.txt
@@ -81,7 +81,9 @@
     method public T! getValue(long playTime);
     method public V getVelocityVector(long playTime);
     method public default boolean isFinished(long playTime);
+    method public boolean isInfinite();
     property public abstract long durationMillis;
+    property public abstract boolean isInfinite;
     property public abstract T! targetValue;
     property public abstract androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter;
   }
@@ -294,9 +296,11 @@
     method public androidx.compose.animation.core.TwoWayConverter<T,V> getTypeConverter();
     method public T! getValue(long playTime);
     method public V getVelocityVector(long playTime);
+    method public boolean isInfinite();
     property public long durationMillis;
     property public final T! initialValue;
     property public final V initialVelocityVector;
+    property public boolean isInfinite;
     property public T! targetValue;
     property public androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter;
   }
@@ -446,7 +450,7 @@
     property public final boolean hasObservers;
   }
 
-  public final class ManualFrameClock implements androidx.compose.runtime.dispatch.MonotonicFrameClock {
+  public final class ManualFrameClock implements androidx.compose.runtime.MonotonicFrameClock {
     ctor @Deprecated public ManualFrameClock(long initialTime, boolean dispatchOnSubscribe);
     ctor public ManualFrameClock(long initialTime);
     method public void advanceClock(long nanos);
@@ -560,8 +564,10 @@
     method public androidx.compose.animation.core.TwoWayConverter<T,V> getTypeConverter();
     method public T! getValue(long playTime);
     method public V getVelocityVector(long playTime);
+    method public boolean isInfinite();
     property public long durationMillis;
     property public final T! initialValue;
+    property public boolean isInfinite;
     property public T! targetValue;
     property public androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter;
   }
@@ -658,6 +664,8 @@
     method public default V getEndVelocity(V start, V end, V startVelocity);
     method public V getValue(long playTime, V start, V end, V startVelocity);
     method public V getVelocity(long playTime, V start, V end, V startVelocity);
+    method public boolean isInfinite();
+    property public abstract boolean isInfinite;
   }
 
   public final class VectorizedAnimationSpecKt {
@@ -681,6 +689,8 @@
   }
 
   public interface VectorizedFiniteAnimationSpec<V extends androidx.compose.animation.core.AnimationVector> extends androidx.compose.animation.core.VectorizedAnimationSpec<V> {
+    method public default boolean isInfinite();
+    property public default boolean isInfinite;
   }
 
   public final class VectorizedFloatAnimationSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedFiniteAnimationSpec<V> {
@@ -692,6 +702,8 @@
 
   public final class VectorizedInfiniteRepeatableSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedAnimationSpec<V> {
     ctor public VectorizedInfiniteRepeatableSpec(androidx.compose.animation.core.VectorizedDurationBasedAnimationSpec<V> animation, androidx.compose.animation.core.RepeatMode repeatMode);
+    method public boolean isInfinite();
+    property public boolean isInfinite;
   }
 
   public final class VectorizedKeyframesSpec<V extends androidx.compose.animation.core.AnimationVector> implements androidx.compose.animation.core.VectorizedDurationBasedAnimationSpec<V> {
diff --git a/compose/animation/animation-core/build.gradle b/compose/animation/animation-core/build.gradle
index 7e1f35a..c55e82a 100644
--- a/compose/animation/animation-core/build.gradle
+++ b/compose/animation/animation-core/build.gradle
@@ -42,10 +42,10 @@
         api "androidx.annotation:annotation:1.1.0"
 
         implementation project(":compose:runtime:runtime")
-        implementation project(":compose:runtime:runtime-dispatch")
         implementation project(":compose:ui:ui-unit")
         implementation project(":compose:ui:ui-util")
         implementation (KOTLIN_STDLIB)
+        api(KOTLIN_COROUTINES_CORE)
 
         testImplementation(ANDROIDX_TEST_RULES)
         testImplementation(ANDROIDX_TEST_RUNNER)
@@ -74,10 +74,10 @@
         sourceSets {
             commonMain.dependencies {
                 implementation project(":compose:runtime:runtime")
-                implementation project(":compose:runtime:runtime-dispatch")
                 implementation project(":compose:ui:ui-unit")
                 implementation project(":compose:ui:ui-util")
                 implementation(KOTLIN_STDLIB_COMMON)
+                api(KOTLIN_COROUTINES_CORE)
             }
             androidMain.dependencies {
                 api "androidx.annotation:annotation:1.1.0"
diff --git a/compose/animation/animation-core/src/androidAndroidTest/kotlin/androidx/compose/animation/core/InfiniteTransitionTest.kt b/compose/animation/animation-core/src/androidAndroidTest/kotlin/androidx/compose/animation/core/InfiniteTransitionTest.kt
index e6f7b5a..0ce2950 100644
--- a/compose/animation/animation-core/src/androidAndroidTest/kotlin/androidx/compose/animation/core/InfiniteTransitionTest.kt
+++ b/compose/animation/animation-core/src/androidAndroidTest/kotlin/androidx/compose/animation/core/InfiniteTransitionTest.kt
@@ -19,7 +19,7 @@
 import androidx.compose.animation.VectorConverter
 import androidx.compose.animation.animateColor
 import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.dispatch.withFrameNanos
+import androidx.compose.runtime.withFrameNanos
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.test.junit4.createComposeRule
diff --git a/compose/animation/animation-core/src/androidAndroidTest/kotlin/androidx/compose/animation/core/SingleValueAnimationTest.kt b/compose/animation/animation-core/src/androidAndroidTest/kotlin/androidx/compose/animation/core/SingleValueAnimationTest.kt
index 31b6963..fe66c47 100644
--- a/compose/animation/animation-core/src/androidAndroidTest/kotlin/androidx/compose/animation/core/SingleValueAnimationTest.kt
+++ b/compose/animation/animation-core/src/androidAndroidTest/kotlin/androidx/compose/animation/core/SingleValueAnimationTest.kt
@@ -19,7 +19,7 @@
 import androidx.compose.animation.animateColorAsState
 import androidx.compose.foundation.layout.Box
 import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.dispatch.withFrameNanos
+import androidx.compose.runtime.withFrameNanos
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.setValue
diff --git a/compose/animation/animation-core/src/androidAndroidTest/kotlin/androidx/compose/animation/core/TransitionTest.kt b/compose/animation/animation-core/src/androidAndroidTest/kotlin/androidx/compose/animation/core/TransitionTest.kt
index e286010..47bccf1 100644
--- a/compose/animation/animation-core/src/androidAndroidTest/kotlin/androidx/compose/animation/core/TransitionTest.kt
+++ b/compose/animation/animation-core/src/androidAndroidTest/kotlin/androidx/compose/animation/core/TransitionTest.kt
@@ -20,7 +20,7 @@
 import androidx.compose.animation.animateColor
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.State
-import androidx.compose.runtime.dispatch.withFrameNanos
+import androidx.compose.runtime.withFrameNanos
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
diff --git a/compose/animation/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
index 5eef343..94ff29b 100644
--- a/compose/animation/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
@@ -53,6 +53,13 @@
     val targetValue: T
 
     /**
+     * Whether or not the [Animation] represents an infinite animation. That is, one that will
+     * not finish by itself, one that needs an external action to stop. For examples, an
+     * indeterminate progress bar, which will only stop when it is removed from the composition.
+     */
+    val isInfinite: Boolean
+
+    /**
      * Returns the value of the animation at the given play time.
      *
      * @param playTime the play time that is used to determine the value of the animation.
@@ -216,6 +223,8 @@
         initialVelocityVector?.copy() ?: typeConverter.convertToVector(initialValue)
             .newInstance()
 
+    override val isInfinite: Boolean get() = animationSpec.isInfinite
+
     override fun getValue(playTime: Long): T {
         return if (playTime < durationMillis) {
             typeConverter.convertFromVector(
@@ -284,6 +293,9 @@
     )
     override val durationMillis: Long
 
+    // DecayAnimation finishes by design
+    override val isInfinite: Boolean = false
+
     /**
      * [DecayAnimation] is an animation that slows down from [initialVelocityVector] as time goes
      * on. [DecayAnimation] is stateless, and it does not have any concept of lifecycle. It
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/InfiniteTransition.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/InfiniteTransition.kt
index e52d481..dd37aaa 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/InfiniteTransition.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/InfiniteTransition.kt
@@ -22,7 +22,7 @@
 import androidx.compose.runtime.SideEffect
 import androidx.compose.runtime.State
 import androidx.compose.runtime.collection.mutableVectorOf
-import androidx.compose.runtime.dispatch.withFrameNanos
+import androidx.compose.runtime.withFrameNanos
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
diff --git a/compose/animation/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
index 73b584f..8d7204e 100644
--- a/compose/animation/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
@@ -16,8 +16,8 @@
 
 package androidx.compose.animation.core
 
-import androidx.compose.runtime.dispatch.BroadcastFrameClock
-import androidx.compose.runtime.dispatch.MonotonicFrameClock
+import androidx.compose.runtime.BroadcastFrameClock
+import androidx.compose.runtime.MonotonicFrameClock
 
 /**
  * A [MonotonicFrameClock] built on a [BroadcastFrameClock] that keeps track of the current time.
diff --git a/compose/animation/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
index ffe6bb5..d866d77 100644
--- a/compose/animation/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
@@ -16,9 +16,9 @@
 
 package androidx.compose.animation.core
 
-import androidx.compose.runtime.dispatch.DefaultMonotonicFrameClock
-import androidx.compose.runtime.dispatch.MonotonicFrameClock
-import androidx.compose.runtime.dispatch.withFrameMillis
+import androidx.compose.runtime.DefaultMonotonicFrameClock
+import androidx.compose.runtime.MonotonicFrameClock
+import androidx.compose.runtime.withFrameMillis
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.CoroutineStart
 import kotlinx.coroutines.ExperimentalCoroutinesApi
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/SuspendAnimation.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/SuspendAnimation.kt
index d6a0072..5594bfd 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/SuspendAnimation.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/SuspendAnimation.kt
@@ -16,7 +16,7 @@
 
 package androidx.compose.animation.core
 
-import androidx.compose.runtime.dispatch.withFrameNanos
+import androidx.compose.runtime.withFrameNanos
 import kotlinx.coroutines.CancellationException
 
 /**
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Transition.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Transition.kt
index d8f3999..fb21aa8 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Transition.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Transition.kt
@@ -25,7 +25,7 @@
 import androidx.compose.runtime.SideEffect
 import androidx.compose.runtime.State
 import androidx.compose.runtime.collection.mutableVectorOf
-import androidx.compose.runtime.dispatch.withFrameNanos
+import androidx.compose.runtime.withFrameNanos
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
diff --git a/compose/animation/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
index 39e4e01..5b5677a 100644
--- a/compose/animation/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
@@ -41,6 +41,13 @@
  */
 interface VectorizedAnimationSpec<V : AnimationVector> {
     /**
+     * Whether or not the [VectorizedAnimationSpec] specifies an infinite animation. That is, one
+     * that will not finish by itself, one that needs an external action to stop. For examples, an
+     * indeterminate progress bar, which will only stop when it is removed from the composition.
+     */
+    val isInfinite: Boolean
+
+    /**
      * Calculates the value of the animation at given the playtime, with the provided start/end
      * values, and start velocity.
      *
@@ -111,7 +118,9 @@
  * [VectorizedSnapSpec], [VectorizedSpringSpec], etc. The [VectorizedAnimationSpec] that does
  * __not__ implement this is: [InfiniteRepeatableSpec].
  */
-interface VectorizedFiniteAnimationSpec<V : AnimationVector> : VectorizedAnimationSpec<V>
+interface VectorizedFiniteAnimationSpec<V : AnimationVector> : VectorizedAnimationSpec<V> {
+    override val isInfinite: Boolean get() = false
+}
 
 /**
  * Base class for [VectorizedAnimationSpec]s that are based on a fixed [durationMillis].
@@ -290,7 +299,9 @@
     private val animation: VectorizedDurationBasedAnimationSpec<V>,
     private val repeatMode: RepeatMode = RepeatMode.Restart
 ) : VectorizedAnimationSpec<V> by
-    VectorizedRepeatableSpec<V>(InfiniteIterations, animation, repeatMode)
+    VectorizedRepeatableSpec<V>(InfiniteIterations, animation, repeatMode) {
+    override val isInfinite: Boolean get() = true
+}
 
 /**
  * This animation takes another [VectorizedDurationBasedAnimationSpec] and plays it
diff --git a/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/IsInfiniteTest.kt b/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/IsInfiniteTest.kt
new file mode 100644
index 0000000..f4a65b4
--- /dev/null
+++ b/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/IsInfiniteTest.kt
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.animation.core
+
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class IsInfiniteTest {
+    @Test
+    fun testTweenIsFinite() {
+        val tweenSpec = tween<Float>()
+        assertThat(tweenSpec.vectorize().isInfinite).isFalse()
+        assertThat(tweenSpec.asAnimation().isInfinite).isFalse()
+    }
+
+    @Test
+    fun testSnapIsFinite() {
+        val snapSpec = snap<Float>()
+        assertThat(snapSpec.vectorize().isInfinite).isFalse()
+        assertThat(snapSpec.asAnimation().isInfinite).isFalse()
+    }
+
+    @Test
+    fun testKeyFramesIsFinite() {
+        val keyFramesSpec = keyframes<Float> { durationMillis = 100 }
+        assertThat(keyFramesSpec.vectorize().isInfinite).isFalse()
+        assertThat(keyFramesSpec.asAnimation().isInfinite).isFalse()
+    }
+
+    @Test
+    fun testSpringIsFinite() {
+        val springSpec = spring<Float>()
+        val animation = springSpec.asAnimation()
+        assertThat(springSpec.vectorize().isInfinite).isFalse()
+        assertThat(animation.isInfinite).isFalse()
+    }
+
+    @Test
+    fun testFiniteRepeatableIsFinite() {
+        val spring = repeatable(10, tween<Float>())
+        assertThat(spring.vectorize().isInfinite).isFalse()
+        assertThat(spring.asAnimation().isInfinite).isFalse()
+    }
+
+    @Test
+    fun testInfiniteRepeatableIsInfinite() {
+        val spring = infiniteRepeatable(tween<Float>())
+        assertThat(spring.vectorize().isInfinite).isTrue()
+        assertThat(spring.asAnimation().isInfinite).isTrue()
+    }
+
+    @Test
+    fun testExponentialDecayAnimationIsFinite() {
+        val decaySpec = exponentialDecay<Float>()
+        assertThat(decaySpec.asAnimation().isInfinite).isFalse()
+    }
+
+    @Test
+    fun testDecayAnimationIsFinite() {
+        val decaySpec = FloatExponentialDecaySpec()
+        assertThat(decaySpec.asAnimation().isInfinite).isFalse()
+    }
+
+    private fun AnimationSpec<Float>.vectorize(): VectorizedAnimationSpec<AnimationVector1D> {
+        return vectorize(Float.VectorConverter)
+    }
+
+    private fun AnimationSpec<Float>.asAnimation(): Animation<Float, AnimationVector1D> {
+        return TargetBasedAnimation(vectorize(), Float.VectorConverter, 0f, 0f)
+    }
+
+    private fun DecayAnimationSpec<Float>.asAnimation(): Animation<Float, AnimationVector1D> {
+        return DecayAnimation(this, Float.VectorConverter, 0f, 0f)
+    }
+
+    private fun FloatDecayAnimationSpec.asAnimation(): Animation<Float, AnimationVector1D> {
+        return DecayAnimation(this, 0f)
+    }
+}
diff --git a/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/SuspendAnimationTest.kt b/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/SuspendAnimationTest.kt
index 9d09692..a734bd1 100644
--- a/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/SuspendAnimationTest.kt
+++ b/compose/animation/animation-core/src/test/java/androidx/compose/animation/core/SuspendAnimationTest.kt
@@ -16,7 +16,7 @@
 
 package androidx.compose.animation.core
 
-import androidx.compose.runtime.dispatch.MonotonicFrameClock
+import androidx.compose.runtime.MonotonicFrameClock
 import androidx.compose.ui.geometry.Offset
 import junit.framework.TestCase.assertEquals
 import junit.framework.TestCase.assertFalse
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/RobolectricComposeTester.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/RobolectricComposeTester.kt
index 48e8991..9fe9099 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/RobolectricComposeTester.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/RobolectricComposeTester.kt
@@ -24,13 +24,13 @@
 import androidx.activity.ComponentActivity
 import androidx.compose.runtime.Composer
 import androidx.compose.runtime.Composition
-import androidx.compose.runtime.EmbeddingContext
 import androidx.compose.runtime.ExperimentalComposeApi
 import androidx.compose.runtime.Recomposer
 import androidx.compose.ui.node.UiApplier
 import androidx.compose.ui.platform.AmbientContext
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.CoroutineStart
+import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.NonCancellable
 import kotlinx.coroutines.launch
@@ -122,9 +122,8 @@
     companion object {
         @OptIn(ExperimentalCoroutinesApi::class)
         private val recomposer = run {
-            val embeddingContext = EmbeddingContext()
             val mainScope = CoroutineScope(
-                NonCancellable + embeddingContext.mainThreadCompositionContext()
+                NonCancellable + Dispatchers.Main
             )
 
             Recomposer(mainScope.coroutineContext).also {
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Clickable.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Clickable.kt
index 99f155a..6032f46 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Clickable.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Clickable.kt
@@ -16,13 +16,14 @@
 
 package androidx.compose.foundation
 
-import androidx.compose.foundation.gestures.detectTapGestures
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.remember
-import androidx.compose.runtime.rememberUpdatedState
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.composed
-import androidx.compose.ui.input.pointer.pointerInput
+import androidx.compose.ui.gesture.doubleTapGestureFilter
+import androidx.compose.ui.gesture.longPressGestureFilter
+import androidx.compose.ui.gesture.pressIndicatorGestureFilter
+import androidx.compose.ui.gesture.tapGestureFilter
 import androidx.compose.ui.platform.debugInspectorInfo
 import androidx.compose.ui.semantics.Role
 import androidx.compose.ui.semantics.disabled
@@ -106,6 +107,7 @@
  * @param onDoubleClick will be called when user double clicks on the element
  * @param onClick will be called when user clicks on the element
  */
+@Suppress("DEPRECATION")
 fun Modifier.clickable(
     enabled: Boolean = true,
     interactionState: InteractionState,
@@ -131,32 +133,25 @@
                 disabled()
             }
         }
-        val onClickState = rememberUpdatedState(onClick)
-        val interactionStateState = rememberUpdatedState(interactionState)
-        val gesture =
+        val interactionUpdate =
             if (enabled) {
-                remember(onDoubleClick, onLongClick) {
-                    Modifier.pointerInput {
-                        detectTapGestures(
-                            onDoubleTap = if (onDoubleClick != null) {
-                                { onDoubleClick() }
-                            } else {
-                                null
-                            },
-                            onLongPress = if (onLongClick != null) {
-                                { onLongClick() }
-                            } else {
-                                null
-                            },
-                            onPress = {
-                                interactionStateState.value.addInteraction(Interaction.Pressed, it)
-                                tryAwaitRelease()
-                                interactionStateState.value.removeInteraction(Interaction.Pressed)
-                            },
-                            onTap = { onClickState.value.invoke() }
-                        )
-                    }
-                }
+                Modifier.pressIndicatorGestureFilter(
+                    onStart = { interactionState.addInteraction(Interaction.Pressed, it) },
+                    onStop = { interactionState.removeInteraction(Interaction.Pressed) },
+                    onCancel = { interactionState.removeInteraction(Interaction.Pressed) }
+                )
+            } else {
+                Modifier
+            }
+        val tap = if (enabled) tapGestureFilter(onTap = { onClick() }) else Modifier
+        val longTap = if (enabled && onLongClick != null) {
+            longPressGestureFilter(onLongPress = { onLongClick() })
+        } else {
+            Modifier
+        }
+        val doubleTap =
+            if (enabled && onDoubleClick != null) {
+                doubleTapGestureFilter(onDoubleTap = { onDoubleClick() })
             } else {
                 Modifier
             }
@@ -166,8 +161,11 @@
             }
         }
         semanticModifier
-            .then(gesture)
+            .then(interactionUpdate)
             .indication(interactionState, indication)
+            .then(tap)
+            .then(longTap)
+            .then(doubleTap)
     },
     inspectorInfo = debugInspectorInfo {
         name = "clickable"
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/animation/Scrolling.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/animation/Scrolling.kt
index 5ea0c9c..5eb7e0d 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/animation/Scrolling.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/animation/Scrolling.kt
@@ -20,7 +20,7 @@
 import androidx.compose.animation.core.VectorConverter
 import androidx.compose.animation.core.spring
 import androidx.compose.foundation.gestures.Scrollable
-import androidx.compose.runtime.dispatch.withFrameMillis
+import androidx.compose.runtime.withFrameMillis
 
 /**
  * Smooth scroll by [value] pixels.
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/selection/Toggleable.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/selection/Toggleable.kt
index ed255f1..504f56c 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/selection/Toggleable.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/selection/Toggleable.kt
@@ -21,14 +21,13 @@
 import androidx.compose.foundation.Interaction
 import androidx.compose.foundation.InteractionState
 import androidx.compose.foundation.Strings
-import androidx.compose.foundation.gestures.detectTapGestures
 import androidx.compose.foundation.indication
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.remember
-import androidx.compose.runtime.rememberUpdatedState
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.composed
-import androidx.compose.ui.input.pointer.pointerInput
+import androidx.compose.ui.gesture.pressIndicatorGestureFilter
+import androidx.compose.ui.gesture.tapGestureFilter
 import androidx.compose.ui.platform.debugInspectorInfo
 import androidx.compose.ui.semantics.Role
 import androidx.compose.ui.semantics.disabled
@@ -231,7 +230,7 @@
     }
 )
 
-@Suppress("ModifierInspectorInfo")
+@Suppress("ModifierInspectorInfo", "DEPRECATION")
 private fun Modifier.toggleableImpl(
     state: ToggleableState,
     enabled: Boolean,
@@ -257,22 +256,17 @@
             disabled()
         }
     }
-    val clickState = rememberUpdatedState(onClick)
-    val interactionStateState = rememberUpdatedState(interactionState)
-    val gestures = if (enabled) {
-        Modifier.pointerInput {
-            detectTapGestures(
-                onTap = { clickState.value.invoke() },
-                onPress = {
-                    interactionStateState.value.addInteraction(Interaction.Pressed, it)
-                    tryAwaitRelease()
-                    interactionStateState.value.removeInteraction(Interaction.Pressed)
-                }
+    val interactionUpdate =
+        if (enabled) {
+            Modifier.pressIndicatorGestureFilter(
+                onStart = { interactionState.addInteraction(Interaction.Pressed, it) },
+                onStop = { interactionState.removeInteraction(Interaction.Pressed) },
+                onCancel = { interactionState.removeInteraction(Interaction.Pressed) }
             )
+        } else {
+            Modifier
         }
-    } else {
-        Modifier
-    }
+    val click = if (enabled) Modifier.tapGestureFilter { onClick() } else Modifier
 
     DisposableEffect(interactionState) {
         onDispose {
@@ -282,5 +276,6 @@
     this
         .then(semantics)
         .indication(interactionState, indication)
-        .then(gestures)
+        .then(interactionUpdate)
+        .then(click)
 }
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/gestures/SuspendingGestureTestUtil.kt b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/gestures/SuspendingGestureTestUtil.kt
index 0726cf4..3d4fe80 100644
--- a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/gestures/SuspendingGestureTestUtil.kt
+++ b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/gestures/SuspendingGestureTestUtil.kt
@@ -25,7 +25,7 @@
 import androidx.compose.runtime.Recomposer
 import androidx.compose.runtime.ControlledComposition
 import androidx.compose.runtime.currentComposer
-import androidx.compose.runtime.dispatch.MonotonicFrameClock
+import androidx.compose.runtime.MonotonicFrameClock
 import androidx.compose.runtime.withRunningRecomposer
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Offset
diff --git a/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/interoperability/Interoperability.kt b/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/interoperability/Interoperability.kt
index c08712c..badd5b5 100644
--- a/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/interoperability/Interoperability.kt
+++ b/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/interoperability/Interoperability.kt
@@ -18,7 +18,7 @@
 // Ignore lint warnings in documentation snippets
 @file:Suppress(
     "unused", "UNUSED_PARAMETER", "UNUSED_VARIABLE", "UNUSED_ANONYMOUS_PARAMETER",
-    "RedundantSuspendModifier", "CascadeIf", "ClassName"
+    "RedundantSuspendModifier", "CascadeIf", "ClassName", "RemoveExplicitTypeArguments"
 )
 
 package androidx.compose.integration.docs.interoperability
@@ -48,6 +48,7 @@
 import androidx.compose.foundation.layout.padding
 import androidx.compose.integration.docs.databinding.ExampleLayoutBinding
 import androidx.compose.material.Button
+import androidx.compose.material.ButtonDefaults
 import androidx.compose.material.FloatingActionButton
 import androidx.compose.material.Icon
 import androidx.compose.material.MaterialTheme
@@ -71,6 +72,7 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.ImageBitmap
 import androidx.compose.ui.graphics.asImageBitmap
+import androidx.compose.ui.platform.AbstractComposeView
 import androidx.compose.ui.platform.AmbientConfiguration
 import androidx.compose.ui.platform.AmbientContext
 import androidx.compose.ui.platform.ComposeView
@@ -630,6 +632,40 @@
 
     private object InteropSnippet10 {
         @Composable
+        fun LoginButton(
+            onClick: () -> Unit,
+            modifier: Modifier = Modifier,
+        ) {
+            Button(
+                colors = ButtonDefaults.buttonColors(
+                    backgroundColor = MaterialTheme.colors.secondary
+                ),
+                onClick = onClick,
+                modifier = modifier,
+            ) {
+                Text(stringResource(R.string.login))
+            }
+        }
+
+        class LoginViewButton @JvmOverloads constructor(
+            context: Context,
+            attrs: AttributeSet? = null,
+            defStyle: Int = 0
+        ) : AbstractComposeView(context, attrs, defStyle) {
+
+            var onClick by mutableStateOf<() -> Unit>({})
+
+            @Composable
+            override fun Content() {
+                YourAppTheme {
+                    LoginButton(onClick)
+                }
+            }
+        }
+    }
+
+    private object InteropSnippet11 {
+        @Composable
         fun SystemBroadcastReceiver(
             systemAction: String,
             onSystemEvent: (intent: Intent?) -> Unit
@@ -686,15 +722,16 @@
     object string {
         const val ok = 4
         const val plane_description = 5
+        const val login = 6
     }
     object dimen {
-        const val padding_small = 6
+        const val padding_small = 7
     }
     object drawable {
-        const val ic_plane = 7
+        const val ic_plane = 8
     }
     object color {
-        const val Blue700 = 8
+        const val Blue700 = 9
     }
 }
 
@@ -730,6 +767,7 @@
 @Composable private fun AppCompatTheme(content: @Composable () -> Unit) { }
 @Composable private fun BlueTheme(content: @Composable () -> Unit) { }
 @Composable private fun PinkTheme(content: @Composable () -> Unit) { }
+@Composable private fun YourAppTheme(content: @Composable () -> Unit) { }
 @Composable private fun ProvideWindowInsets(content: @Composable () -> Unit) { }
 @Composable private fun Icon() { }
 
diff --git a/compose/runtime/runtime-dispatch/api/current.txt b/compose/runtime/runtime-dispatch/api/current.txt
index f8f939f..059af93 100644
--- a/compose/runtime/runtime-dispatch/api/current.txt
+++ b/compose/runtime/runtime-dispatch/api/current.txt
@@ -1,60 +1,13 @@
 // Signature format: 4.0
 package androidx.compose.runtime.dispatch {
 
-  public final class ActualAndroidKt {
-    method public static androidx.compose.runtime.dispatch.MonotonicFrameClock getDefaultMonotonicFrameClock();
-  }
-
-  public final class AndroidUiDispatcher extends kotlinx.coroutines.CoroutineDispatcher {
-    method public void dispatch(kotlin.coroutines.CoroutineContext context, Runnable block);
-    method public android.view.Choreographer getChoreographer();
-    method public androidx.compose.runtime.dispatch.MonotonicFrameClock getFrameClock();
-    property public final android.view.Choreographer choreographer;
-    property public final androidx.compose.runtime.dispatch.MonotonicFrameClock frameClock;
-    field public static final androidx.compose.runtime.dispatch.AndroidUiDispatcher.Companion Companion;
-  }
-
-  public static final class AndroidUiDispatcher.Companion {
-    method public kotlin.coroutines.CoroutineContext getCurrentThread();
-    method public kotlin.coroutines.CoroutineContext getMain();
-    property public final kotlin.coroutines.CoroutineContext CurrentThread;
-    property public final kotlin.coroutines.CoroutineContext Main;
-  }
-
-  public final class AndroidUiDispatcherKt {
-  }
-
-  public final class AndroidUiFrameClock implements androidx.compose.runtime.dispatch.MonotonicFrameClock {
-    ctor public AndroidUiFrameClock(android.view.Choreographer choreographer);
-    method public android.view.Choreographer getChoreographer();
-    method public suspend <R> Object? withFrameNanos(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
-    property public final android.view.Choreographer choreographer;
-  }
-
-  public final class BroadcastFrameClock implements androidx.compose.runtime.dispatch.MonotonicFrameClock {
-    ctor public BroadcastFrameClock(kotlin.jvm.functions.Function0<kotlin.Unit>? onNewAwaiters);
-    ctor public BroadcastFrameClock();
-    method public void cancel(optional java.util.concurrent.CancellationException cancellationException);
-    method public boolean getHasAwaiters();
-    method public void sendFrame(long timeNanos);
-    method public suspend <R> Object? withFrameNanos(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
-    property public final boolean hasAwaiters;
-  }
-
-  public interface MonotonicFrameClock extends kotlin.coroutines.CoroutineContext.Element {
-    method public default kotlin.coroutines.CoroutineContext.Key<?> getKey();
-    method public suspend <R> Object? withFrameNanos(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
-    property public default kotlin.coroutines.CoroutineContext.Key<?> key;
-    field public static final androidx.compose.runtime.dispatch.MonotonicFrameClock.Key Key;
-  }
-
-  public static final class MonotonicFrameClock.Key implements kotlin.coroutines.CoroutineContext.Key<androidx.compose.runtime.dispatch.MonotonicFrameClock> {
+  public final class ExpectKt {
+    method @Deprecated public static androidx.compose.runtime.MonotonicFrameClock getDefaultMonotonicFrameClock();
   }
 
   public final class MonotonicFrameClockKt {
-    method public static suspend inline <R> Object? withFrameMillis(androidx.compose.runtime.dispatch.MonotonicFrameClock, kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
-    method public static suspend <R> Object? withFrameMillis(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
-    method public static suspend <R> Object? withFrameNanos(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
+    method @Deprecated public static suspend <R> Object? withFrameMillis(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
+    method @Deprecated public static suspend <R> Object? withFrameNanos(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
   }
 
 }
diff --git a/compose/runtime/runtime-dispatch/api/public_plus_experimental_current.txt b/compose/runtime/runtime-dispatch/api/public_plus_experimental_current.txt
index f8f939f..059af93 100644
--- a/compose/runtime/runtime-dispatch/api/public_plus_experimental_current.txt
+++ b/compose/runtime/runtime-dispatch/api/public_plus_experimental_current.txt
@@ -1,60 +1,13 @@
 // Signature format: 4.0
 package androidx.compose.runtime.dispatch {
 
-  public final class ActualAndroidKt {
-    method public static androidx.compose.runtime.dispatch.MonotonicFrameClock getDefaultMonotonicFrameClock();
-  }
-
-  public final class AndroidUiDispatcher extends kotlinx.coroutines.CoroutineDispatcher {
-    method public void dispatch(kotlin.coroutines.CoroutineContext context, Runnable block);
-    method public android.view.Choreographer getChoreographer();
-    method public androidx.compose.runtime.dispatch.MonotonicFrameClock getFrameClock();
-    property public final android.view.Choreographer choreographer;
-    property public final androidx.compose.runtime.dispatch.MonotonicFrameClock frameClock;
-    field public static final androidx.compose.runtime.dispatch.AndroidUiDispatcher.Companion Companion;
-  }
-
-  public static final class AndroidUiDispatcher.Companion {
-    method public kotlin.coroutines.CoroutineContext getCurrentThread();
-    method public kotlin.coroutines.CoroutineContext getMain();
-    property public final kotlin.coroutines.CoroutineContext CurrentThread;
-    property public final kotlin.coroutines.CoroutineContext Main;
-  }
-
-  public final class AndroidUiDispatcherKt {
-  }
-
-  public final class AndroidUiFrameClock implements androidx.compose.runtime.dispatch.MonotonicFrameClock {
-    ctor public AndroidUiFrameClock(android.view.Choreographer choreographer);
-    method public android.view.Choreographer getChoreographer();
-    method public suspend <R> Object? withFrameNanos(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
-    property public final android.view.Choreographer choreographer;
-  }
-
-  public final class BroadcastFrameClock implements androidx.compose.runtime.dispatch.MonotonicFrameClock {
-    ctor public BroadcastFrameClock(kotlin.jvm.functions.Function0<kotlin.Unit>? onNewAwaiters);
-    ctor public BroadcastFrameClock();
-    method public void cancel(optional java.util.concurrent.CancellationException cancellationException);
-    method public boolean getHasAwaiters();
-    method public void sendFrame(long timeNanos);
-    method public suspend <R> Object? withFrameNanos(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
-    property public final boolean hasAwaiters;
-  }
-
-  public interface MonotonicFrameClock extends kotlin.coroutines.CoroutineContext.Element {
-    method public default kotlin.coroutines.CoroutineContext.Key<?> getKey();
-    method public suspend <R> Object? withFrameNanos(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
-    property public default kotlin.coroutines.CoroutineContext.Key<?> key;
-    field public static final androidx.compose.runtime.dispatch.MonotonicFrameClock.Key Key;
-  }
-
-  public static final class MonotonicFrameClock.Key implements kotlin.coroutines.CoroutineContext.Key<androidx.compose.runtime.dispatch.MonotonicFrameClock> {
+  public final class ExpectKt {
+    method @Deprecated public static androidx.compose.runtime.MonotonicFrameClock getDefaultMonotonicFrameClock();
   }
 
   public final class MonotonicFrameClockKt {
-    method public static suspend inline <R> Object? withFrameMillis(androidx.compose.runtime.dispatch.MonotonicFrameClock, kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
-    method public static suspend <R> Object? withFrameMillis(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
-    method public static suspend <R> Object? withFrameNanos(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
+    method @Deprecated public static suspend <R> Object? withFrameMillis(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
+    method @Deprecated public static suspend <R> Object? withFrameNanos(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
   }
 
 }
diff --git a/compose/runtime/runtime-dispatch/api/restricted_current.txt b/compose/runtime/runtime-dispatch/api/restricted_current.txt
index f8f939f..059af93 100644
--- a/compose/runtime/runtime-dispatch/api/restricted_current.txt
+++ b/compose/runtime/runtime-dispatch/api/restricted_current.txt
@@ -1,60 +1,13 @@
 // Signature format: 4.0
 package androidx.compose.runtime.dispatch {
 
-  public final class ActualAndroidKt {
-    method public static androidx.compose.runtime.dispatch.MonotonicFrameClock getDefaultMonotonicFrameClock();
-  }
-
-  public final class AndroidUiDispatcher extends kotlinx.coroutines.CoroutineDispatcher {
-    method public void dispatch(kotlin.coroutines.CoroutineContext context, Runnable block);
-    method public android.view.Choreographer getChoreographer();
-    method public androidx.compose.runtime.dispatch.MonotonicFrameClock getFrameClock();
-    property public final android.view.Choreographer choreographer;
-    property public final androidx.compose.runtime.dispatch.MonotonicFrameClock frameClock;
-    field public static final androidx.compose.runtime.dispatch.AndroidUiDispatcher.Companion Companion;
-  }
-
-  public static final class AndroidUiDispatcher.Companion {
-    method public kotlin.coroutines.CoroutineContext getCurrentThread();
-    method public kotlin.coroutines.CoroutineContext getMain();
-    property public final kotlin.coroutines.CoroutineContext CurrentThread;
-    property public final kotlin.coroutines.CoroutineContext Main;
-  }
-
-  public final class AndroidUiDispatcherKt {
-  }
-
-  public final class AndroidUiFrameClock implements androidx.compose.runtime.dispatch.MonotonicFrameClock {
-    ctor public AndroidUiFrameClock(android.view.Choreographer choreographer);
-    method public android.view.Choreographer getChoreographer();
-    method public suspend <R> Object? withFrameNanos(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
-    property public final android.view.Choreographer choreographer;
-  }
-
-  public final class BroadcastFrameClock implements androidx.compose.runtime.dispatch.MonotonicFrameClock {
-    ctor public BroadcastFrameClock(kotlin.jvm.functions.Function0<kotlin.Unit>? onNewAwaiters);
-    ctor public BroadcastFrameClock();
-    method public void cancel(optional java.util.concurrent.CancellationException cancellationException);
-    method public boolean getHasAwaiters();
-    method public void sendFrame(long timeNanos);
-    method public suspend <R> Object? withFrameNanos(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
-    property public final boolean hasAwaiters;
-  }
-
-  public interface MonotonicFrameClock extends kotlin.coroutines.CoroutineContext.Element {
-    method public default kotlin.coroutines.CoroutineContext.Key<?> getKey();
-    method public suspend <R> Object? withFrameNanos(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
-    property public default kotlin.coroutines.CoroutineContext.Key<?> key;
-    field public static final androidx.compose.runtime.dispatch.MonotonicFrameClock.Key Key;
-  }
-
-  public static final class MonotonicFrameClock.Key implements kotlin.coroutines.CoroutineContext.Key<androidx.compose.runtime.dispatch.MonotonicFrameClock> {
+  public final class ExpectKt {
+    method @Deprecated public static androidx.compose.runtime.MonotonicFrameClock getDefaultMonotonicFrameClock();
   }
 
   public final class MonotonicFrameClockKt {
-    method public static suspend inline <R> Object? withFrameMillis(androidx.compose.runtime.dispatch.MonotonicFrameClock, kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
-    method public static suspend <R> Object? withFrameMillis(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
-    method public static suspend <R> Object? withFrameNanos(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
+    method @Deprecated public static suspend <R> Object? withFrameMillis(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
+    method @Deprecated public static suspend <R> Object? withFrameNanos(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
   }
 
 }
diff --git a/compose/runtime/runtime-dispatch/build.gradle b/compose/runtime/runtime-dispatch/build.gradle
index 7694ed5..7967eab 100644
--- a/compose/runtime/runtime-dispatch/build.gradle
+++ b/compose/runtime/runtime-dispatch/build.gradle
@@ -40,6 +40,8 @@
         api(KOTLIN_COROUTINES_CORE)
         api "androidx.annotation:annotation:1.1.0"
 
+        api(project(":compose:ui:ui"))
+
         implementation(KOTLIN_STDLIB_COMMON)
         implementation(KOTLIN_STDLIB)
         implementation("androidx.core:core-ktx:1.1.0")
@@ -69,6 +71,7 @@
             commonMain.dependencies {
                 implementation(KOTLIN_STDLIB_COMMON)
                 api(KOTLIN_COROUTINES_CORE)
+                api(project(":compose:ui:ui"))
             }
             jvmMain.dependencies {
                 implementation(KOTLIN_STDLIB)
diff --git a/compose/runtime/runtime-dispatch/src/androidAndroidTest/kotlin/androidx/compose/runtime/dispatch/TestActivity.kt b/compose/runtime/runtime-dispatch/src/androidAndroidTest/kotlin/androidx/compose/runtime/dispatch/TestActivity.kt
deleted file mode 100644
index 83fa029..0000000
--- a/compose/runtime/runtime-dispatch/src/androidAndroidTest/kotlin/androidx/compose/runtime/dispatch/TestActivity.kt
+++ /dev/null
@@ -1,21 +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.compose.runtime.dispatch
-
-import androidx.core.app.ComponentActivity
-
-class TestActivity : ComponentActivity()
\ No newline at end of file
diff --git a/compose/runtime/runtime-dispatch/src/androidMain/kotlin/androidx/compose/runtime/dispatch/ActualAndroid.kt b/compose/runtime/runtime-dispatch/src/androidMain/kotlin/androidx/compose/runtime/dispatch/ActualAndroid.kt
deleted file mode 100644
index 1beb3a0..0000000
--- a/compose/runtime/runtime-dispatch/src/androidMain/kotlin/androidx/compose/runtime/dispatch/ActualAndroid.kt
+++ /dev/null
@@ -1,45 +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.compose.runtime.dispatch
-
-import android.os.Looper
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.delay
-import kotlinx.coroutines.withContext
-
-/**
- * This is an inaccurate implementation that will only be used when running linked against
- * Android SDK stubs in host-side tests. A real implementation should synchronize with the
- * device's default display's vsync rate.
- */
-private object SdkStubsFallbackFrameClock : MonotonicFrameClock {
-    private const val DefaultFrameDelay = 16L // milliseconds
-
-    override suspend fun <R> withFrameNanos(onFrame: (frameTimeNanos: Long) -> R): R =
-        withContext(Dispatchers.Main) {
-            delay(DefaultFrameDelay)
-            onFrame(System.nanoTime())
-        }
-}
-
-actual val DefaultMonotonicFrameClock: MonotonicFrameClock by lazy {
-    // When linked against Android SDK stubs and running host-side tests, APIs such as
-    // Looper.getMainLooper() that will never return null on a real device will return null.
-    // This branch offers an alternative solution.
-    if (Looper.getMainLooper() != null) AndroidUiDispatcher.Main[MonotonicFrameClock]!!
-    else SdkStubsFallbackFrameClock
-}
diff --git a/compose/runtime/runtime-dispatch/src/commonMain/kotlin/androidx/compose/runtime/dispatch/Expect.kt b/compose/runtime/runtime-dispatch/src/commonMain/kotlin/androidx/compose/runtime/dispatch/Expect.kt
index 44010e8..d8e4796 100644
--- a/compose/runtime/runtime-dispatch/src/commonMain/kotlin/androidx/compose/runtime/dispatch/Expect.kt
+++ b/compose/runtime/runtime-dispatch/src/commonMain/kotlin/androidx/compose/runtime/dispatch/Expect.kt
@@ -16,12 +16,17 @@
 
 package androidx.compose.runtime.dispatch
 
+import androidx.compose.runtime.withFrameMillis
+import androidx.compose.runtime.withFrameNanos
+
 /**
  * The [MonotonicFrameClock] used by [withFrameNanos] and [withFrameMillis] if one is not present
  * in the calling [kotlin.coroutines.CoroutineContext].
  */
-// Implementor's note:
-// This frame clock implementation should try to synchronize with the vsync rate of the device's
-// default display. Without this synchronization, any usage of this default clock will result
-// in inconsistent animation frame timing and associated visual artifacts.
-expect val DefaultMonotonicFrameClock: MonotonicFrameClock
+@Suppress("DEPRECATION")
+@Deprecated(
+    "Moved to androidx.compose.runtime",
+    ReplaceWith("androidx.compose.runtime.DefaultMonotonicFrameClock")
+)
+val DefaultMonotonicFrameClock: MonotonicFrameClock
+    get() = androidx.compose.runtime.DefaultMonotonicFrameClock
diff --git a/compose/runtime/runtime-dispatch/src/commonMain/kotlin/androidx/compose/runtime/dispatch/MonotonicFrameClock.kt b/compose/runtime/runtime-dispatch/src/commonMain/kotlin/androidx/compose/runtime/dispatch/MonotonicFrameClock.kt
index 157c309..3882c52 100644
--- a/compose/runtime/runtime-dispatch/src/commonMain/kotlin/androidx/compose/runtime/dispatch/MonotonicFrameClock.kt
+++ b/compose/runtime/runtime-dispatch/src/commonMain/kotlin/androidx/compose/runtime/dispatch/MonotonicFrameClock.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Android Open Source Project
+ * Copyright 2021 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,91 +16,29 @@
 
 package androidx.compose.runtime.dispatch
 
-import kotlin.coroutines.CoroutineContext
-import kotlin.coroutines.coroutineContext
-
-/**
- * Provides a time source for display frames and the ability to perform an action on the next frame.
- * This may be used for matching timing with the refresh rate of a display or otherwise
- * synchronizing work with a desired frame rate.
- */
-interface MonotonicFrameClock : CoroutineContext.Element {
-    /**
-     * Suspends until a new frame is requested, immediately invokes [onFrame] with the frame time
-     * in nanoseconds in the calling context of frame dispatch, then resumes with the result from
-     * [onFrame].
-     *
-     * `frameTimeNanos` should be used when calculating animation time deltas from frame to frame
-     * as it may be normalized to the target time for the frame, not necessarily a direct,
-     * "now" value.
-     *
-     * The time base of the value provided by [withFrameNanos] is implementation defined.
-     * Time values provided are monotonically increasing; after a call to [withFrameNanos]
-     * completes it must not provide the same value again for a subsequent call.
-     */
-    suspend fun <R> withFrameNanos(onFrame: (frameTimeNanos: Long) -> R): R
-
-    override val key: CoroutineContext.Key<*> get() = Key
-
-    companion object Key : CoroutineContext.Key<MonotonicFrameClock>
-}
+@Deprecated("Moved to androidx.compose.runtime in the compose:runtime artifact")
+typealias MonotonicFrameClock = androidx.compose.runtime.MonotonicFrameClock
 
 /**
  * Suspends until a new frame is requested, immediately invokes [onFrame] with the frame time
  * in nanoseconds in the calling context of frame dispatch, then resumes with the result from
  * [onFrame].
- *
- * `frameTimeNanos` should be used when calculating animation time deltas from frame to frame
- * as it may be normalized to the target time for the frame, not necessarily a direct,
- * "now" value.
- *
- * The time base of the value provided by [MonotonicFrameClock.withFrameMillis] is
- * implementation defined. Time values provided are monotonically increasing; after a call to
- * [MonotonicFrameClock.withFrameMillis] completes it must not provide the same value again for
- * a subsequent call.
  */
-@Suppress("UnnecessaryLambdaCreation")
-suspend inline fun <R> MonotonicFrameClock.withFrameMillis(
-    crossinline onFrame: (frameTimeMillis: Long) -> R
-): R = withFrameNanos { onFrame(it / 1_000_000L) }
-
-/**
- * Suspends until a new frame is requested, immediately invokes [onFrame] with the frame time
- * in nanoseconds in the calling context of frame dispatch, then resumes with the result from
- * [onFrame].
- *
- * `frameTimeNanos` should be used when calculating animation time deltas from frame to frame
- * as it may be normalized to the target time for the frame, not necessarily a direct,
- * "now" value.
- *
- * The time base of the value provided by [withFrameNanos] is implementation defined.
- * Time values provided are monotonically increasing; after a call to [withFrameNanos]
- * completes it must not provide the same value again for a subsequent call.
- *
- * This function will invoke [MonotonicFrameClock.withFrameNanos] using the calling
- * [CoroutineContext]'s [MonotonicFrameClock] or a default frame clock if one is not present
- * in the [CoroutineContext].
- */
+@Deprecated(
+    "Moved to androidx.compose.runtime",
+    ReplaceWith("androidx.compose.runtime.withFrameNanos(onFrame)")
+)
 suspend fun <R> withFrameNanos(onFrame: (frameTimeMillis: Long) -> R): R =
-    (coroutineContext[MonotonicFrameClock] ?: DefaultMonotonicFrameClock).withFrameNanos(onFrame)
+    androidx.compose.runtime.withFrameNanos(onFrame)
 
 /**
  * Suspends until a new frame is requested, immediately invokes [onFrame] with the frame time
  * in nanoseconds in the calling context of frame dispatch, then resumes with the result from
  * [onFrame].
- *
- * `frameTimeNanos` should be used when calculating animation time deltas from frame to frame
- * as it may be normalized to the target time for the frame, not necessarily a direct,
- * "now" value.
- *
- * The time base of the value provided by [MonotonicFrameClock.withFrameMillis] is
- * implementation defined. Time values provided are monotonically increasing; after a call to
- * [MonotonicFrameClock.withFrameMillis] completes it must not provide the same value again for
- * a subsequent call.
- *
- * This function will invoke [MonotonicFrameClock.withFrameNanos] using the calling
- * [CoroutineContext]'s [MonotonicFrameClock] or a default frame clock if one is not present
- * in the [CoroutineContext].
  */
+@Deprecated(
+    "Moved to androidx.compose.runtime",
+    ReplaceWith("androidx.compose.runtime.withFrameMillis(onFrame)")
+)
 suspend fun <R> withFrameMillis(onFrame: (frameTimeMillis: Long) -> R): R =
-    (coroutineContext[MonotonicFrameClock] ?: DefaultMonotonicFrameClock).withFrameMillis(onFrame)
+    androidx.compose.runtime.withFrameMillis(onFrame)
diff --git a/compose/runtime/runtime-dispatch/src/desktopMain/kotlin/androidx/compose/runtime/dispatch/ActualDesktop.kt b/compose/runtime/runtime-dispatch/src/desktopMain/kotlin/androidx/compose/runtime/dispatch/ActualDesktop.kt
deleted file mode 100644
index 711bd90..0000000
--- a/compose/runtime/runtime-dispatch/src/desktopMain/kotlin/androidx/compose/runtime/dispatch/ActualDesktop.kt
+++ /dev/null
@@ -1,49 +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.compose.runtime.dispatch
-
-import kotlinx.coroutines.delay
-import kotlinx.coroutines.yield
-import java.awt.DisplayMode
-import java.awt.GraphicsEnvironment
-
-// TODO implement local Recomposer in each Window, so each Window can have own MonotonicFrameClock.
-//  It is needed for smooth animations and for the case when user have multiple windows on multiple
-//  monitors with different refresh rates.
-//  see https://github.com/JetBrains/compose-jb/issues/137
-actual val DefaultMonotonicFrameClock: MonotonicFrameClock by lazy {
-    object : MonotonicFrameClock {
-        override suspend fun <R> withFrameNanos(
-            onFrame: (Long) -> R
-        ): R {
-            if (GraphicsEnvironment.isHeadless()) {
-                yield()
-            } else {
-                delay(1000L / getFramesPerSecond())
-            }
-            return onFrame(System.nanoTime())
-        }
-
-        private fun getFramesPerSecond(): Int {
-            val refreshRate = GraphicsEnvironment
-                .getLocalGraphicsEnvironment()
-                .screenDevices.maxOfOrNull { it.displayMode.refreshRate }
-                ?: DisplayMode.REFRESH_RATE_UNKNOWN
-            return if (refreshRate != DisplayMode.REFRESH_RATE_UNKNOWN) refreshRate else 60
-        }
-    }
-}
\ No newline at end of file
diff --git a/compose/runtime/runtime/api/current.txt b/compose/runtime/runtime/api/current.txt
index dfe7b99..833d1e5 100644
--- a/compose/runtime/runtime/api/current.txt
+++ b/compose/runtime/runtime/api/current.txt
@@ -17,7 +17,7 @@
   }
 
   public final class ActualAndroidKt {
-    method public static androidx.compose.runtime.EmbeddingContext EmbeddingContext();
+    method public static androidx.compose.runtime.MonotonicFrameClock getDefaultMonotonicFrameClock();
   }
 
   public final class ActualJvmKt {
@@ -51,6 +51,16 @@
   public final class BitwiseOperatorsKt {
   }
 
+  public final class BroadcastFrameClock implements androidx.compose.runtime.MonotonicFrameClock {
+    ctor public BroadcastFrameClock(kotlin.jvm.functions.Function0<kotlin.Unit>? onNewAwaiters);
+    ctor public BroadcastFrameClock();
+    method public void cancel(optional java.util.concurrent.CancellationException cancellationException);
+    method public boolean getHasAwaiters();
+    method public void sendFrame(long timeNanos);
+    method public suspend <R> Object? withFrameNanos(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
+    property public final boolean hasAwaiters;
+  }
+
   @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget}) public @interface Composable {
   }
 
@@ -219,11 +229,6 @@
     method public inline androidx.compose.runtime.DisposableEffectResult onDispose(kotlin.jvm.functions.Function0<kotlin.Unit> onDisposeEffect);
   }
 
-  public interface EmbeddingContext {
-    method public boolean isMainThread();
-    method public kotlin.coroutines.CoroutineContext mainThreadCompositionContext();
-  }
-
   public final class ExpectKt {
   }
 
@@ -269,6 +274,22 @@
     method @androidx.compose.runtime.InternalComposeApi public static void resetSourceInfo();
   }
 
+  public interface MonotonicFrameClock extends kotlin.coroutines.CoroutineContext.Element {
+    method public default kotlin.coroutines.CoroutineContext.Key<?> getKey();
+    method public suspend <R> Object? withFrameNanos(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
+    property public default kotlin.coroutines.CoroutineContext.Key<?> key;
+    field public static final androidx.compose.runtime.MonotonicFrameClock.Key Key;
+  }
+
+  public static final class MonotonicFrameClock.Key implements kotlin.coroutines.CoroutineContext.Key<androidx.compose.runtime.MonotonicFrameClock> {
+  }
+
+  public final class MonotonicFrameClockKt {
+    method public static suspend inline <R> Object? withFrameMillis(androidx.compose.runtime.MonotonicFrameClock, kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
+    method public static suspend <R> Object? withFrameMillis(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
+    method public static suspend <R> Object? withFrameNanos(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
+  }
+
   @androidx.compose.runtime.Stable public interface MutableState<T> extends androidx.compose.runtime.State<T> {
     method public operator T! component1();
     method public operator kotlin.jvm.functions.Function1<T,kotlin.Unit> component2();
@@ -294,8 +315,8 @@
   @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget}) public @interface NoLiveLiterals {
   }
 
-  public final class PausableMonotonicFrameClock implements androidx.compose.runtime.dispatch.MonotonicFrameClock {
-    ctor public PausableMonotonicFrameClock(androidx.compose.runtime.dispatch.MonotonicFrameClock frameClock);
+  public final class PausableMonotonicFrameClock implements androidx.compose.runtime.MonotonicFrameClock {
+    ctor public PausableMonotonicFrameClock(androidx.compose.runtime.MonotonicFrameClock frameClock);
     method public boolean isPaused();
     method public void pause();
     method public void resume();
diff --git a/compose/runtime/runtime/api/public_plus_experimental_current.txt b/compose/runtime/runtime/api/public_plus_experimental_current.txt
index dfe7b99..833d1e5 100644
--- a/compose/runtime/runtime/api/public_plus_experimental_current.txt
+++ b/compose/runtime/runtime/api/public_plus_experimental_current.txt
@@ -17,7 +17,7 @@
   }
 
   public final class ActualAndroidKt {
-    method public static androidx.compose.runtime.EmbeddingContext EmbeddingContext();
+    method public static androidx.compose.runtime.MonotonicFrameClock getDefaultMonotonicFrameClock();
   }
 
   public final class ActualJvmKt {
@@ -51,6 +51,16 @@
   public final class BitwiseOperatorsKt {
   }
 
+  public final class BroadcastFrameClock implements androidx.compose.runtime.MonotonicFrameClock {
+    ctor public BroadcastFrameClock(kotlin.jvm.functions.Function0<kotlin.Unit>? onNewAwaiters);
+    ctor public BroadcastFrameClock();
+    method public void cancel(optional java.util.concurrent.CancellationException cancellationException);
+    method public boolean getHasAwaiters();
+    method public void sendFrame(long timeNanos);
+    method public suspend <R> Object? withFrameNanos(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
+    property public final boolean hasAwaiters;
+  }
+
   @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget}) public @interface Composable {
   }
 
@@ -219,11 +229,6 @@
     method public inline androidx.compose.runtime.DisposableEffectResult onDispose(kotlin.jvm.functions.Function0<kotlin.Unit> onDisposeEffect);
   }
 
-  public interface EmbeddingContext {
-    method public boolean isMainThread();
-    method public kotlin.coroutines.CoroutineContext mainThreadCompositionContext();
-  }
-
   public final class ExpectKt {
   }
 
@@ -269,6 +274,22 @@
     method @androidx.compose.runtime.InternalComposeApi public static void resetSourceInfo();
   }
 
+  public interface MonotonicFrameClock extends kotlin.coroutines.CoroutineContext.Element {
+    method public default kotlin.coroutines.CoroutineContext.Key<?> getKey();
+    method public suspend <R> Object? withFrameNanos(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
+    property public default kotlin.coroutines.CoroutineContext.Key<?> key;
+    field public static final androidx.compose.runtime.MonotonicFrameClock.Key Key;
+  }
+
+  public static final class MonotonicFrameClock.Key implements kotlin.coroutines.CoroutineContext.Key<androidx.compose.runtime.MonotonicFrameClock> {
+  }
+
+  public final class MonotonicFrameClockKt {
+    method public static suspend inline <R> Object? withFrameMillis(androidx.compose.runtime.MonotonicFrameClock, kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
+    method public static suspend <R> Object? withFrameMillis(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
+    method public static suspend <R> Object? withFrameNanos(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
+  }
+
   @androidx.compose.runtime.Stable public interface MutableState<T> extends androidx.compose.runtime.State<T> {
     method public operator T! component1();
     method public operator kotlin.jvm.functions.Function1<T,kotlin.Unit> component2();
@@ -294,8 +315,8 @@
   @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget}) public @interface NoLiveLiterals {
   }
 
-  public final class PausableMonotonicFrameClock implements androidx.compose.runtime.dispatch.MonotonicFrameClock {
-    ctor public PausableMonotonicFrameClock(androidx.compose.runtime.dispatch.MonotonicFrameClock frameClock);
+  public final class PausableMonotonicFrameClock implements androidx.compose.runtime.MonotonicFrameClock {
+    ctor public PausableMonotonicFrameClock(androidx.compose.runtime.MonotonicFrameClock frameClock);
     method public boolean isPaused();
     method public void pause();
     method public void resume();
diff --git a/compose/runtime/runtime/api/restricted_current.txt b/compose/runtime/runtime/api/restricted_current.txt
index 48d7439..07e1499 100644
--- a/compose/runtime/runtime/api/restricted_current.txt
+++ b/compose/runtime/runtime/api/restricted_current.txt
@@ -17,7 +17,7 @@
   }
 
   public final class ActualAndroidKt {
-    method public static androidx.compose.runtime.EmbeddingContext EmbeddingContext();
+    method public static androidx.compose.runtime.MonotonicFrameClock getDefaultMonotonicFrameClock();
   }
 
   public final class ActualJvmKt {
@@ -52,6 +52,16 @@
   public final class BitwiseOperatorsKt {
   }
 
+  public final class BroadcastFrameClock implements androidx.compose.runtime.MonotonicFrameClock {
+    ctor public BroadcastFrameClock(kotlin.jvm.functions.Function0<kotlin.Unit>? onNewAwaiters);
+    ctor public BroadcastFrameClock();
+    method public void cancel(optional java.util.concurrent.CancellationException cancellationException);
+    method public boolean getHasAwaiters();
+    method public void sendFrame(long timeNanos);
+    method public suspend <R> Object? withFrameNanos(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
+    property public final boolean hasAwaiters;
+  }
+
   @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget}) public @interface Composable {
   }
 
@@ -242,11 +252,6 @@
     method public inline androidx.compose.runtime.DisposableEffectResult onDispose(kotlin.jvm.functions.Function0<kotlin.Unit> onDisposeEffect);
   }
 
-  public interface EmbeddingContext {
-    method public boolean isMainThread();
-    method public kotlin.coroutines.CoroutineContext mainThreadCompositionContext();
-  }
-
   public final class ExpectKt {
     method @kotlin.PublishedApi internal static inline <R> R! synchronized(Object lock, kotlin.jvm.functions.Function0<? extends R> block);
   }
@@ -293,6 +298,22 @@
     method @androidx.compose.runtime.InternalComposeApi public static void resetSourceInfo();
   }
 
+  public interface MonotonicFrameClock extends kotlin.coroutines.CoroutineContext.Element {
+    method public default kotlin.coroutines.CoroutineContext.Key<?> getKey();
+    method public suspend <R> Object? withFrameNanos(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
+    property public default kotlin.coroutines.CoroutineContext.Key<?> key;
+    field public static final androidx.compose.runtime.MonotonicFrameClock.Key Key;
+  }
+
+  public static final class MonotonicFrameClock.Key implements kotlin.coroutines.CoroutineContext.Key<androidx.compose.runtime.MonotonicFrameClock> {
+  }
+
+  public final class MonotonicFrameClockKt {
+    method public static suspend inline <R> Object? withFrameMillis(androidx.compose.runtime.MonotonicFrameClock, kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
+    method public static suspend <R> Object? withFrameMillis(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
+    method public static suspend <R> Object? withFrameNanos(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
+  }
+
   @androidx.compose.runtime.Stable public interface MutableState<T> extends androidx.compose.runtime.State<T> {
     method public operator T! component1();
     method public operator kotlin.jvm.functions.Function1<T,kotlin.Unit> component2();
@@ -318,8 +339,8 @@
   @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget, kotlin.annotation.AnnotationTarget}) public @interface NoLiveLiterals {
   }
 
-  public final class PausableMonotonicFrameClock implements androidx.compose.runtime.dispatch.MonotonicFrameClock {
-    ctor public PausableMonotonicFrameClock(androidx.compose.runtime.dispatch.MonotonicFrameClock frameClock);
+  public final class PausableMonotonicFrameClock implements androidx.compose.runtime.MonotonicFrameClock {
+    ctor public PausableMonotonicFrameClock(androidx.compose.runtime.MonotonicFrameClock frameClock);
     method public boolean isPaused();
     method public void pause();
     method public void resume();
diff --git a/compose/runtime/runtime/build.gradle b/compose/runtime/runtime/build.gradle
index 3bec43a..b0d5f77 100644
--- a/compose/runtime/runtime/build.gradle
+++ b/compose/runtime/runtime/build.gradle
@@ -38,7 +38,6 @@
          * corresponding block below
          */
 
-        api project(':compose:runtime:runtime-dispatch')
         api(KOTLIN_COROUTINES_ANDROID)
 
         implementation "androidx.annotation:annotation:1.1.0"
@@ -76,7 +75,6 @@
             commonMain.dependencies {
                 implementation(KOTLIN_STDLIB_COMMON)
                 implementation(KOTLIN_COROUTINES_CORE)
-                api project(':compose:runtime:runtime-dispatch')
                 implementation "org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3"
             }
             jvmMain.dependencies {
diff --git a/compose/runtime/runtime/integration-tests/src/androidAndroidTest/kotlin/androidx/compose/runtime/SideEffectTests.kt b/compose/runtime/runtime/integration-tests/src/androidAndroidTest/kotlin/androidx/compose/runtime/SideEffectTests.kt
index 386994c..e5a5fd3 100644
--- a/compose/runtime/runtime/integration-tests/src/androidAndroidTest/kotlin/androidx/compose/runtime/SideEffectTests.kt
+++ b/compose/runtime/runtime/integration-tests/src/androidAndroidTest/kotlin/androidx/compose/runtime/SideEffectTests.kt
@@ -17,8 +17,6 @@
 package androidx.compose.runtime
 
 import android.view.Choreographer
-import androidx.compose.runtime.dispatch.MonotonicFrameClock
-import androidx.compose.runtime.dispatch.withFrameNanos
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
 import kotlinx.coroutines.channels.Channel
diff --git a/compose/runtime/runtime/integration-tests/src/androidAndroidTest/kotlin/androidx/compose/runtime/SuspendingEffectsTests.kt b/compose/runtime/runtime/integration-tests/src/androidAndroidTest/kotlin/androidx/compose/runtime/SuspendingEffectsTests.kt
index 443d8f3..75c706f 100644
--- a/compose/runtime/runtime/integration-tests/src/androidAndroidTest/kotlin/androidx/compose/runtime/SuspendingEffectsTests.kt
+++ b/compose/runtime/runtime/integration-tests/src/androidAndroidTest/kotlin/androidx/compose/runtime/SuspendingEffectsTests.kt
@@ -17,8 +17,6 @@
 package androidx.compose.runtime
 
 import android.view.Choreographer
-import androidx.compose.runtime.dispatch.MonotonicFrameClock
-import androidx.compose.runtime.dispatch.withFrameNanos
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
 import kotlinx.coroutines.CoroutineScope
diff --git a/compose/runtime/runtime/src/androidMain/kotlin/androidx/compose/runtime/ActualAndroid.kt b/compose/runtime/runtime/src/androidMain/kotlin/androidx/compose/runtime/ActualAndroid.kt
index 86e3745..1aab3c2 100644
--- a/compose/runtime/runtime/src/androidMain/kotlin/androidx/compose/runtime/ActualAndroid.kt
+++ b/compose/runtime/runtime/src/androidMain/kotlin/androidx/compose/runtime/ActualAndroid.kt
@@ -17,29 +17,12 @@
 package androidx.compose.runtime
 
 import android.os.Looper
-import androidx.compose.runtime.dispatch.AndroidUiDispatcher
+import android.view.Choreographer
 import kotlinx.coroutines.Dispatchers
-import kotlin.coroutines.CoroutineContext
-
-private object AndroidEmbeddingContext : EmbeddingContext {
-
-    override fun isMainThread(): Boolean {
-        return Looper.myLooper() == Looper.getMainLooper()
-    }
-
-    override fun mainThreadCompositionContext(): CoroutineContext {
-        return MainAndroidUiContext
-    }
-}
-
-actual fun EmbeddingContext(): EmbeddingContext = AndroidEmbeddingContext
-
-// TODO: Our host-side tests still grab the Android actuals based on SDK stubs that return null.
-// Satisfy their dependencies.
-private val MainAndroidUiContext: CoroutineContext by lazy {
-    if (Looper.getMainLooper() != null) AndroidUiDispatcher.Main
-    else Dispatchers.Main
-}
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.suspendCancellableCoroutine
+import kotlinx.coroutines.withContext
 
 internal actual object Trace {
     actual fun beginSection(name: String): Any? {
@@ -55,3 +38,47 @@
 internal actual typealias MainThread = androidx.annotation.MainThread
 
 internal actual typealias CheckResult = androidx.annotation.CheckResult
+
+/**
+ * This is an inaccurate implementation that will only be used when running linked against
+ * Android SDK stubs in host-side tests. A real implementation should synchronize with the
+ * device's default display's vsync rate.
+ */
+private object SdkStubsFallbackFrameClock : MonotonicFrameClock {
+    private const val DefaultFrameDelay = 16L // milliseconds
+
+    override suspend fun <R> withFrameNanos(onFrame: (frameTimeNanos: Long) -> R): R =
+        withContext(Dispatchers.Main) {
+            delay(DefaultFrameDelay)
+            onFrame(System.nanoTime())
+        }
+}
+
+private object DefaultChoreographerFrameClock : MonotonicFrameClock {
+    private val choreographer = runBlocking(Dispatchers.Main.immediate) {
+        Choreographer.getInstance()
+    }
+
+    override suspend fun <R> withFrameNanos(
+        onFrame: (frameTimeNanos: Long) -> R
+    ): R = suspendCancellableCoroutine<R> { co ->
+        val callback = Choreographer.FrameCallback { frameTimeNanos ->
+            co.resumeWith(runCatching { onFrame(frameTimeNanos) })
+        }
+        choreographer.postFrameCallback(callback)
+        co.invokeOnCancellation { choreographer.removeFrameCallback(callback) }
+    }
+}
+
+// For local testing
+private const val DisallowDefaultMonotonicFrameClock = false
+
+actual val DefaultMonotonicFrameClock: MonotonicFrameClock by lazy {
+    if (DisallowDefaultMonotonicFrameClock) error("Disallowed use of DefaultMonotonicFrameClock")
+
+    // When linked against Android SDK stubs and running host-side tests, APIs such as
+    // Looper.getMainLooper() that will never return null on a real device will return null.
+    // This branch offers an alternative solution.
+    if (Looper.getMainLooper() != null) DefaultChoreographerFrameClock
+    else SdkStubsFallbackFrameClock
+}
diff --git a/compose/runtime/runtime-dispatch/src/commonMain/kotlin/androidx/compose/runtime/dispatch/BroadcastFrameClock.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/BroadcastFrameClock.kt
similarity index 97%
rename from compose/runtime/runtime-dispatch/src/commonMain/kotlin/androidx/compose/runtime/dispatch/BroadcastFrameClock.kt
rename to compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/BroadcastFrameClock.kt
index 79db2ef..e5112e9 100644
--- a/compose/runtime/runtime-dispatch/src/commonMain/kotlin/androidx/compose/runtime/dispatch/BroadcastFrameClock.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/BroadcastFrameClock.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Android Open Source Project
+ * Copyright 2021 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package androidx.compose.runtime.dispatch
+package androidx.compose.runtime
 
 import kotlinx.coroutines.CancellationException
 import kotlinx.coroutines.suspendCancellableCoroutine
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/EmbeddingContext.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/EmbeddingContext.kt
deleted file mode 100644
index d2958264..0000000
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/EmbeddingContext.kt
+++ /dev/null
@@ -1,26 +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.compose.runtime
-
-import kotlin.coroutines.CoroutineContext
-
-interface EmbeddingContext {
-    fun isMainThread(): Boolean
-    fun mainThreadCompositionContext(): CoroutineContext
-}
-
-expect fun EmbeddingContext(): EmbeddingContext
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Expect.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Expect.kt
index 9edecf5..2fa9cba 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Expect.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Expect.kt
@@ -68,3 +68,13 @@
 expect annotation class CheckResult(
     val suggest: String
 )
+
+/**
+ * The [MonotonicFrameClock] used by [withFrameNanos] and [withFrameMillis] if one is not present
+ * in the calling [kotlin.coroutines.CoroutineContext].
+ */
+// Implementor's note:
+// This frame clock implementation should try to synchronize with the vsync rate of the device's
+// default display. Without this synchronization, any usage of this default clock will result
+// in inconsistent animation frame timing and associated visual artifacts.
+expect val DefaultMonotonicFrameClock: MonotonicFrameClock
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/MonotonicFrameClock.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/MonotonicFrameClock.kt
new file mode 100644
index 0000000..17d6bff
--- /dev/null
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/MonotonicFrameClock.kt
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.runtime
+
+import kotlin.coroutines.CoroutineContext
+import kotlin.coroutines.coroutineContext
+
+/**
+ * Provides a time source for display frames and the ability to perform an action on the next frame.
+ * This may be used for matching timing with the refresh rate of a display or otherwise
+ * synchronizing work with a desired frame rate.
+ */
+interface MonotonicFrameClock : CoroutineContext.Element {
+    /**
+     * Suspends until a new frame is requested, immediately invokes [onFrame] with the frame time
+     * in nanoseconds in the calling context of frame dispatch, then resumes with the result from
+     * [onFrame].
+     *
+     * `frameTimeNanos` should be used when calculating animation time deltas from frame to frame
+     * as it may be normalized to the target time for the frame, not necessarily a direct,
+     * "now" value.
+     *
+     * The time base of the value provided by [withFrameNanos] is implementation defined.
+     * Time values provided are monotonically increasing; after a call to [withFrameNanos]
+     * completes it must not provide the same value again for a subsequent call.
+     */
+    suspend fun <R> withFrameNanos(onFrame: (frameTimeNanos: Long) -> R): R
+
+    override val key: CoroutineContext.Key<*> get() = Key
+
+    companion object Key : CoroutineContext.Key<MonotonicFrameClock>
+}
+
+/**
+ * Suspends until a new frame is requested, immediately invokes [onFrame] with the frame time
+ * in nanoseconds in the calling context of frame dispatch, then resumes with the result from
+ * [onFrame].
+ *
+ * `frameTimeNanos` should be used when calculating animation time deltas from frame to frame
+ * as it may be normalized to the target time for the frame, not necessarily a direct,
+ * "now" value.
+ *
+ * The time base of the value provided by [MonotonicFrameClock.withFrameMillis] is
+ * implementation defined. Time values provided are monotonically increasing; after a call to
+ * [MonotonicFrameClock.withFrameMillis] completes it must not provide the same value again for
+ * a subsequent call.
+ */
+@Suppress("UnnecessaryLambdaCreation")
+suspend inline fun <R> MonotonicFrameClock.withFrameMillis(
+    crossinline onFrame: (frameTimeMillis: Long) -> R
+): R = withFrameNanos { onFrame(it / 1_000_000L) }
+
+/**
+ * Suspends until a new frame is requested, immediately invokes [onFrame] with the frame time
+ * in nanoseconds in the calling context of frame dispatch, then resumes with the result from
+ * [onFrame].
+ *
+ * `frameTimeNanos` should be used when calculating animation time deltas from frame to frame
+ * as it may be normalized to the target time for the frame, not necessarily a direct,
+ * "now" value.
+ *
+ * The time base of the value provided by [withFrameNanos] is implementation defined.
+ * Time values provided are monotonically increasing; after a call to [withFrameNanos]
+ * completes it must not provide the same value again for a subsequent call.
+ *
+ * This function will invoke [MonotonicFrameClock.withFrameNanos] using the calling
+ * [CoroutineContext]'s [MonotonicFrameClock] or a default frame clock if one is not present
+ * in the [CoroutineContext].
+ */
+suspend fun <R> withFrameNanos(onFrame: (frameTimeMillis: Long) -> R): R =
+    (coroutineContext[MonotonicFrameClock] ?: DefaultMonotonicFrameClock).withFrameNanos(onFrame)
+
+/**
+ * Suspends until a new frame is requested, immediately invokes [onFrame] with the frame time
+ * in nanoseconds in the calling context of frame dispatch, then resumes with the result from
+ * [onFrame].
+ *
+ * `frameTimeNanos` should be used when calculating animation time deltas from frame to frame
+ * as it may be normalized to the target time for the frame, not necessarily a direct,
+ * "now" value.
+ *
+ * The time base of the value provided by [MonotonicFrameClock.withFrameMillis] is
+ * implementation defined. Time values provided are monotonically increasing; after a call to
+ * [MonotonicFrameClock.withFrameMillis] completes it must not provide the same value again for
+ * a subsequent call.
+ *
+ * This function will invoke [MonotonicFrameClock.withFrameNanos] using the calling
+ * [CoroutineContext]'s [MonotonicFrameClock] or a default frame clock if one is not present
+ * in the [CoroutineContext].
+ */
+suspend fun <R> withFrameMillis(onFrame: (frameTimeMillis: Long) -> R): R =
+    (coroutineContext[MonotonicFrameClock] ?: DefaultMonotonicFrameClock).withFrameMillis(onFrame)
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/PausableMonotonicFrameClock.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/PausableMonotonicFrameClock.kt
index 2312443..b8a9246c 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/PausableMonotonicFrameClock.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/PausableMonotonicFrameClock.kt
@@ -16,8 +16,6 @@
 
 package androidx.compose.runtime
 
-import androidx.compose.runtime.dispatch.MonotonicFrameClock
-
 /**
  * A [MonotonicFrameClock] wrapper that can be [pause]d and [resume]d.
  *
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Recomposer.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Recomposer.kt
index 3dbe264..8cc6928 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Recomposer.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Recomposer.kt
@@ -16,9 +16,6 @@
 
 package androidx.compose.runtime
 
-import androidx.compose.runtime.dispatch.BroadcastFrameClock
-import androidx.compose.runtime.dispatch.DefaultMonotonicFrameClock
-import androidx.compose.runtime.dispatch.MonotonicFrameClock
 import androidx.compose.runtime.snapshots.MutableSnapshot
 import androidx.compose.runtime.snapshots.Snapshot
 import androidx.compose.runtime.snapshots.SnapshotApplyResult
diff --git a/compose/runtime/runtime/src/desktopMain/kotlin/androidx/compose/runtime/ActualDesktop.kt b/compose/runtime/runtime/src/desktopMain/kotlin/androidx/compose/runtime/ActualDesktop.kt
index 84b217f..6da8ac9b 100644
--- a/compose/runtime/runtime/src/desktopMain/kotlin/androidx/compose/runtime/ActualDesktop.kt
+++ b/compose/runtime/runtime/src/desktopMain/kotlin/androidx/compose/runtime/ActualDesktop.kt
@@ -16,23 +16,10 @@
 
 package androidx.compose.runtime
 
-import androidx.compose.runtime.dispatch.DefaultMonotonicFrameClock
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.swing.Swing
-import javax.swing.SwingUtilities
-import kotlin.coroutines.CoroutineContext
-
-class SwingEmbeddingContext : EmbeddingContext {
-    override fun isMainThread(): Boolean {
-        return SwingUtilities.isEventDispatchThread()
-    }
-
-    override fun mainThreadCompositionContext(): CoroutineContext {
-        return Dispatchers.Swing + DefaultMonotonicFrameClock
-    }
-}
-
-actual fun EmbeddingContext(): EmbeddingContext = SwingEmbeddingContext()
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.yield
+import java.awt.DisplayMode
+import java.awt.GraphicsEnvironment
 
 internal actual object Trace {
     actual fun beginSection(name: String): Any? {
@@ -104,4 +91,31 @@
 
 // TODO(igotti): do we need actual processing for those?
 actual annotation class MainThread()
-actual annotation class CheckResult(actual val suggest: String)
\ No newline at end of file
+actual annotation class CheckResult(actual val suggest: String)
+
+// TODO implement local Recomposer in each Window, so each Window can have own MonotonicFrameClock.
+//  It is needed for smooth animations and for the case when user have multiple windows on multiple
+//  monitors with different refresh rates.
+//  see https://github.com/JetBrains/compose-jb/issues/137
+actual val DefaultMonotonicFrameClock: MonotonicFrameClock by lazy {
+    object : MonotonicFrameClock {
+        override suspend fun <R> withFrameNanos(
+            onFrame: (Long) -> R
+        ): R {
+            if (GraphicsEnvironment.isHeadless()) {
+                yield()
+            } else {
+                delay(1000L / getFramesPerSecond())
+            }
+            return onFrame(System.nanoTime())
+        }
+
+        private fun getFramesPerSecond(): Int {
+            val refreshRate = GraphicsEnvironment
+                .getLocalGraphicsEnvironment()
+                .screenDevices.maxOfOrNull { it.displayMode.refreshRate }
+                ?: DisplayMode.REFRESH_RATE_UNKNOWN
+            return if (refreshRate != DisplayMode.REFRESH_RATE_UNKNOWN) refreshRate else 60
+        }
+    }
+}
diff --git a/compose/runtime/runtime-dispatch/src/test/kotlin/androidx/compose/runtime/dispatch/BroadcastFrameClockTest.kt b/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/BroadcastFrameClockTest.kt
similarity index 91%
rename from compose/runtime/runtime-dispatch/src/test/kotlin/androidx/compose/runtime/dispatch/BroadcastFrameClockTest.kt
rename to compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/BroadcastFrameClockTest.kt
index 7204639..b348e9b 100644
--- a/compose/runtime/runtime-dispatch/src/test/kotlin/androidx/compose/runtime/dispatch/BroadcastFrameClockTest.kt
+++ b/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/BroadcastFrameClockTest.kt
@@ -29,7 +29,7 @@
 class BroadcastFrameClockTest {
     @Test
     fun sendAndReceiveFrames() = runBlockingTest {
-        val clock = BroadcastFrameClock()
+        val clock = androidx.compose.runtime.BroadcastFrameClock()
 
         val frameAwaiter = async { clock.withFrameNanos { it } }
 
@@ -49,7 +49,7 @@
 
     @Test
     fun cancelClock() = runBlockingTest {
-        val clock = BroadcastFrameClock()
+        val clock = androidx.compose.runtime.BroadcastFrameClock()
         val frameAwaiter = async { clock.withFrameNanos { it } }
 
         clock.cancel()
@@ -64,7 +64,7 @@
 
     @Test
     fun failClockWhenNewAwaitersNotified() = runBlockingTest {
-        val clock = BroadcastFrameClock {
+        val clock = androidx.compose.runtime.BroadcastFrameClock {
             throw CancellationException("failed frame clock")
         }
 
diff --git a/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/mock/TestMonotonicFrameClock.kt b/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/mock/TestMonotonicFrameClock.kt
index d7d6eb2..87ba6a4 100644
--- a/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/mock/TestMonotonicFrameClock.kt
+++ b/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/mock/TestMonotonicFrameClock.kt
@@ -20,7 +20,7 @@
  */
 package androidx.compose.runtime.mock
 
-import androidx.compose.runtime.dispatch.MonotonicFrameClock
+import androidx.compose.runtime.MonotonicFrameClock
 import kotlinx.coroutines.CancellableContinuation
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
diff --git a/compose/test-utils/src/androidMain/kotlin/androidx/compose/testutils/AndroidComposeTestCaseRunner.kt b/compose/test-utils/src/androidMain/kotlin/androidx/compose/testutils/AndroidComposeTestCaseRunner.kt
index 61e30d5..78b7b8f 100644
--- a/compose/test-utils/src/androidMain/kotlin/androidx/compose/testutils/AndroidComposeTestCaseRunner.kt
+++ b/compose/test-utils/src/androidMain/kotlin/androidx/compose/testutils/AndroidComposeTestCaseRunner.kt
@@ -31,7 +31,7 @@
 import androidx.activity.ComponentActivity
 import androidx.compose.runtime.ExperimentalComposeApi
 import androidx.compose.runtime.Recomposer
-import androidx.compose.runtime.dispatch.MonotonicFrameClock
+import androidx.compose.runtime.MonotonicFrameClock
 import androidx.compose.runtime.snapshots.Snapshot
 import androidx.compose.ui.platform.ViewRootForTest
 import androidx.compose.ui.platform.setContent
diff --git a/compose/ui/ui-inspection/build.gradle b/compose/ui/ui-inspection/build.gradle
index b0b764a..e562922 100644
--- a/compose/ui/ui-inspection/build.gradle
+++ b/compose/ui/ui-inspection/build.gradle
@@ -79,3 +79,7 @@
         ]
     }
 }
+
+inspection {
+    name = "compose-ui-inspection.jar"
+}
\ No newline at end of file
diff --git a/compose/ui/ui-test-junit4/src/androidAndroidTest/kotlin/androidx/compose/ui/test/junit4/ComposeIdlingResourceTest.kt b/compose/ui/ui-test-junit4/src/androidAndroidTest/kotlin/androidx/compose/ui/test/junit4/ComposeIdlingResourceTest.kt
index b54bb4e..3895bc6 100644
--- a/compose/ui/ui-test-junit4/src/androidAndroidTest/kotlin/androidx/compose/ui/test/junit4/ComposeIdlingResourceTest.kt
+++ b/compose/ui/ui-test-junit4/src/androidAndroidTest/kotlin/androidx/compose/ui/test/junit4/ComposeIdlingResourceTest.kt
@@ -30,11 +30,11 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.ExperimentalComposeApi
 import androidx.compose.runtime.State
-import androidx.compose.runtime.dispatch.withFrameNanos
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.rememberCoroutineScope
 import androidx.compose.runtime.snapshots.Snapshot
+import androidx.compose.runtime.withFrameNanos
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.geometry.Size
diff --git a/compose/ui/ui-test/api/current.txt b/compose/ui/ui-test/api/current.txt
index 343c76f..61c92645 100644
--- a/compose/ui/ui-test/api/current.txt
+++ b/compose/ui/ui-test/api/current.txt
@@ -279,7 +279,7 @@
   public final class TestContext {
   }
 
-  @kotlinx.coroutines.ExperimentalCoroutinesApi public final class TestMonotonicFrameClock implements androidx.compose.runtime.dispatch.MonotonicFrameClock {
+  @kotlinx.coroutines.ExperimentalCoroutinesApi public final class TestMonotonicFrameClock implements androidx.compose.runtime.MonotonicFrameClock {
     ctor public TestMonotonicFrameClock(kotlinx.coroutines.CoroutineScope coroutineScope, kotlinx.coroutines.test.DelayController delayController, long frameDelayNanos);
     method public long getFrameDelayNanos();
     method public boolean getHasAwaiters();
diff --git a/compose/ui/ui-test/api/public_plus_experimental_current.txt b/compose/ui/ui-test/api/public_plus_experimental_current.txt
index 343c76f..61c92645 100644
--- a/compose/ui/ui-test/api/public_plus_experimental_current.txt
+++ b/compose/ui/ui-test/api/public_plus_experimental_current.txt
@@ -279,7 +279,7 @@
   public final class TestContext {
   }
 
-  @kotlinx.coroutines.ExperimentalCoroutinesApi public final class TestMonotonicFrameClock implements androidx.compose.runtime.dispatch.MonotonicFrameClock {
+  @kotlinx.coroutines.ExperimentalCoroutinesApi public final class TestMonotonicFrameClock implements androidx.compose.runtime.MonotonicFrameClock {
     ctor public TestMonotonicFrameClock(kotlinx.coroutines.CoroutineScope coroutineScope, kotlinx.coroutines.test.DelayController delayController, long frameDelayNanos);
     method public long getFrameDelayNanos();
     method public boolean getHasAwaiters();
diff --git a/compose/ui/ui-test/api/restricted_current.txt b/compose/ui/ui-test/api/restricted_current.txt
index 343c76f..61c92645 100644
--- a/compose/ui/ui-test/api/restricted_current.txt
+++ b/compose/ui/ui-test/api/restricted_current.txt
@@ -279,7 +279,7 @@
   public final class TestContext {
   }
 
-  @kotlinx.coroutines.ExperimentalCoroutinesApi public final class TestMonotonicFrameClock implements androidx.compose.runtime.dispatch.MonotonicFrameClock {
+  @kotlinx.coroutines.ExperimentalCoroutinesApi public final class TestMonotonicFrameClock implements androidx.compose.runtime.MonotonicFrameClock {
     ctor public TestMonotonicFrameClock(kotlinx.coroutines.CoroutineScope coroutineScope, kotlinx.coroutines.test.DelayController delayController, long frameDelayNanos);
     method public long getFrameDelayNanos();
     method public boolean getHasAwaiters();
diff --git a/compose/ui/ui-test/src/androidAndroidTest/kotlin/androidx/compose/ui/test/TestMonotonicFrameClockTest.kt b/compose/ui/ui-test/src/androidAndroidTest/kotlin/androidx/compose/ui/test/TestMonotonicFrameClockTest.kt
index adbf9ab..75bfc07 100644
--- a/compose/ui/ui-test/src/androidAndroidTest/kotlin/androidx/compose/ui/test/TestMonotonicFrameClockTest.kt
+++ b/compose/ui/ui-test/src/androidAndroidTest/kotlin/androidx/compose/ui/test/TestMonotonicFrameClockTest.kt
@@ -16,7 +16,7 @@
 
 package androidx.compose.ui.test
 
-import androidx.compose.runtime.dispatch.withFrameNanos
+import androidx.compose.runtime.withFrameNanos
 import androidx.test.filters.SmallTest
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.launch
diff --git a/compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/AndroidInputDispatcher.kt b/compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/AndroidInputDispatcher.kt
index b43fcc5..adadb76 100644
--- a/compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/AndroidInputDispatcher.kt
+++ b/compose/ui/ui-test/src/androidMain/kotlin/androidx/compose/ui/test/AndroidInputDispatcher.kt
@@ -25,7 +25,7 @@
 import android.view.MotionEvent.ACTION_POINTER_INDEX_SHIFT
 import android.view.MotionEvent.ACTION_POINTER_UP
 import android.view.MotionEvent.ACTION_UP
-import androidx.compose.runtime.dispatch.AndroidUiDispatcher
+import androidx.compose.ui.platform.AndroidUiDispatcher
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.node.RootForTest
 import androidx.compose.ui.platform.ViewRootForTest
diff --git a/compose/ui/ui-test/src/jvmMain/kotlin/androidx/compose/ui/test/TestMonotonicFrameClock.kt b/compose/ui/ui-test/src/jvmMain/kotlin/androidx/compose/ui/test/TestMonotonicFrameClock.kt
index 289a08d..f68fbfe 100644
--- a/compose/ui/ui-test/src/jvmMain/kotlin/androidx/compose/ui/test/TestMonotonicFrameClock.kt
+++ b/compose/ui/ui-test/src/jvmMain/kotlin/androidx/compose/ui/test/TestMonotonicFrameClock.kt
@@ -16,7 +16,7 @@
 
 package androidx.compose.ui.test
 
-import androidx.compose.runtime.dispatch.MonotonicFrameClock
+import androidx.compose.runtime.MonotonicFrameClock
 import kotlinx.coroutines.CancellableContinuation
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
diff --git a/compose/ui/ui/api/current.txt b/compose/ui/ui/api/current.txt
index aea0509..b39e99c 100644
--- a/compose/ui/ui/api/current.txt
+++ b/compose/ui/ui/api/current.txt
@@ -2185,6 +2185,32 @@
   public final class AndroidComposeViewKt {
   }
 
+  public final class AndroidUiDispatcher extends kotlinx.coroutines.CoroutineDispatcher {
+    method public void dispatch(kotlin.coroutines.CoroutineContext context, Runnable block);
+    method public android.view.Choreographer getChoreographer();
+    method public androidx.compose.runtime.MonotonicFrameClock getFrameClock();
+    property public final android.view.Choreographer choreographer;
+    property public final androidx.compose.runtime.MonotonicFrameClock frameClock;
+    field public static final androidx.compose.ui.platform.AndroidUiDispatcher.Companion Companion;
+  }
+
+  public static final class AndroidUiDispatcher.Companion {
+    method public kotlin.coroutines.CoroutineContext getCurrentThread();
+    method public kotlin.coroutines.CoroutineContext getMain();
+    property public final kotlin.coroutines.CoroutineContext CurrentThread;
+    property public final kotlin.coroutines.CoroutineContext Main;
+  }
+
+  public final class AndroidUiDispatcherKt {
+  }
+
+  public final class AndroidUiFrameClock implements androidx.compose.runtime.MonotonicFrameClock {
+    ctor public AndroidUiFrameClock(android.view.Choreographer choreographer);
+    method public android.view.Choreographer getChoreographer();
+    method public suspend <R> Object? withFrameNanos(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
+    property public final android.view.Choreographer choreographer;
+  }
+
   public final class AndroidUriHandler implements androidx.compose.ui.platform.UriHandler {
     ctor public AndroidUriHandler(android.content.Context context);
     method public void openUri(String uri);
diff --git a/compose/ui/ui/api/public_plus_experimental_current.txt b/compose/ui/ui/api/public_plus_experimental_current.txt
index aea0509..b39e99c 100644
--- a/compose/ui/ui/api/public_plus_experimental_current.txt
+++ b/compose/ui/ui/api/public_plus_experimental_current.txt
@@ -2185,6 +2185,32 @@
   public final class AndroidComposeViewKt {
   }
 
+  public final class AndroidUiDispatcher extends kotlinx.coroutines.CoroutineDispatcher {
+    method public void dispatch(kotlin.coroutines.CoroutineContext context, Runnable block);
+    method public android.view.Choreographer getChoreographer();
+    method public androidx.compose.runtime.MonotonicFrameClock getFrameClock();
+    property public final android.view.Choreographer choreographer;
+    property public final androidx.compose.runtime.MonotonicFrameClock frameClock;
+    field public static final androidx.compose.ui.platform.AndroidUiDispatcher.Companion Companion;
+  }
+
+  public static final class AndroidUiDispatcher.Companion {
+    method public kotlin.coroutines.CoroutineContext getCurrentThread();
+    method public kotlin.coroutines.CoroutineContext getMain();
+    property public final kotlin.coroutines.CoroutineContext CurrentThread;
+    property public final kotlin.coroutines.CoroutineContext Main;
+  }
+
+  public final class AndroidUiDispatcherKt {
+  }
+
+  public final class AndroidUiFrameClock implements androidx.compose.runtime.MonotonicFrameClock {
+    ctor public AndroidUiFrameClock(android.view.Choreographer choreographer);
+    method public android.view.Choreographer getChoreographer();
+    method public suspend <R> Object? withFrameNanos(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
+    property public final android.view.Choreographer choreographer;
+  }
+
   public final class AndroidUriHandler implements androidx.compose.ui.platform.UriHandler {
     ctor public AndroidUriHandler(android.content.Context context);
     method public void openUri(String uri);
diff --git a/compose/ui/ui/api/restricted_current.txt b/compose/ui/ui/api/restricted_current.txt
index bdcf71e..3ff9be4b 100644
--- a/compose/ui/ui/api/restricted_current.txt
+++ b/compose/ui/ui/api/restricted_current.txt
@@ -2247,6 +2247,32 @@
   public final class AndroidComposeViewKt {
   }
 
+  public final class AndroidUiDispatcher extends kotlinx.coroutines.CoroutineDispatcher {
+    method public void dispatch(kotlin.coroutines.CoroutineContext context, Runnable block);
+    method public android.view.Choreographer getChoreographer();
+    method public androidx.compose.runtime.MonotonicFrameClock getFrameClock();
+    property public final android.view.Choreographer choreographer;
+    property public final androidx.compose.runtime.MonotonicFrameClock frameClock;
+    field public static final androidx.compose.ui.platform.AndroidUiDispatcher.Companion Companion;
+  }
+
+  public static final class AndroidUiDispatcher.Companion {
+    method public kotlin.coroutines.CoroutineContext getCurrentThread();
+    method public kotlin.coroutines.CoroutineContext getMain();
+    property public final kotlin.coroutines.CoroutineContext CurrentThread;
+    property public final kotlin.coroutines.CoroutineContext Main;
+  }
+
+  public final class AndroidUiDispatcherKt {
+  }
+
+  public final class AndroidUiFrameClock implements androidx.compose.runtime.MonotonicFrameClock {
+    ctor public AndroidUiFrameClock(android.view.Choreographer choreographer);
+    method public android.view.Choreographer getChoreographer();
+    method public suspend <R> Object? withFrameNanos(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
+    property public final android.view.Choreographer choreographer;
+  }
+
   public final class AndroidUriHandler implements androidx.compose.ui.platform.UriHandler {
     ctor public AndroidUriHandler(android.content.Context context);
     method public void openUri(String uri);
diff --git a/compose/ui/ui/build.gradle b/compose/ui/ui/build.gradle
index b50126b..9f93aaf 100644
--- a/compose/ui/ui/build.gradle
+++ b/compose/ui/ui/build.gradle
@@ -72,6 +72,7 @@
 
         testImplementation(ANDROIDX_TEST_RULES)
         testImplementation(ANDROIDX_TEST_RUNNER)
+        testImplementation(KOTLIN_COROUTINES_TEST)
         testImplementation(JUNIT)
         testImplementation(TRUTH)
         testImplementation(MOCKITO_CORE)
@@ -87,6 +88,8 @@
         androidTestImplementation(ANDROIDX_TEST_UIAUTOMATOR)
         androidTestImplementation(ANDROIDX_TEST_RULES)
         androidTestImplementation(ANDROIDX_TEST_RUNNER)
+        androidTestImplementation(ANDROIDX_TEST_EXT_KTX)
+        androidTestImplementation(KOTLIN_COROUTINES_TEST)
         androidTestImplementation(ESPRESSO_CORE)
         androidTestImplementation(JUNIT)
         androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy)
@@ -104,6 +107,7 @@
         androidTestImplementation project(":compose:ui:ui-test-junit4")
         androidTestImplementation project(":test-screenshot")
         androidTestImplementation "androidx.recyclerview:recyclerview:1.1.0"
+        androidTestImplementation("androidx.core:core-ktx:1.1.0")
 
         lintChecks project(":compose:ui:ui-lint")
         lintPublish project(":compose:ui:ui-lint")
@@ -166,6 +170,7 @@
             test.dependencies {
                 implementation(ANDROIDX_TEST_RULES)
                 implementation(ANDROIDX_TEST_RUNNER)
+                implementation(KOTLIN_COROUTINES_TEST)
                 implementation(JUNIT)
                 implementation(TRUTH)
                 implementation(MOCKITO_CORE)
@@ -183,6 +188,8 @@
                 implementation(ANDROIDX_TEST_UIAUTOMATOR)
                 implementation(ANDROIDX_TEST_RULES)
                 implementation(ANDROIDX_TEST_RUNNER)
+                implementation(ANDROIDX_TEST_EXT_KTX)
+                implementation(KOTLIN_COROUTINES_TEST)
                 implementation(ESPRESSO_CORE)
                 implementation(JUNIT)
                 implementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy)
@@ -200,6 +207,7 @@
                 implementation project(":compose:ui:ui-test-junit4")
                 implementation project(":test-screenshot")
                 implementation "androidx.recyclerview:recyclerview:1.1.0"
+                implementation("androidx.core:core-ktx:1.1.0")
             }
 
             desktopTest.dependencies {
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/MemoryLeakTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/MemoryLeakTest.kt
index c4198df..91f3634 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/MemoryLeakTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/MemoryLeakTest.kt
@@ -24,7 +24,7 @@
 import androidx.compose.foundation.text.BasicText
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.dispatch.AndroidUiDispatcher
+import androidx.compose.ui.platform.AndroidUiDispatcher
 import androidx.compose.testutils.ComposeTestCase
 import androidx.compose.testutils.createAndroidComposeBenchmarkRunner
 import androidx.compose.ui.platform.setContent
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/ClickNotPlacedChildTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/ClickNotPlacedChildTest.kt
new file mode 100644
index 0000000..3fbc3d6
--- /dev/null
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/ClickNotPlacedChildTest.kt
@@ -0,0 +1,83 @@
+/*
+ * 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.input
+
+import android.os.Build
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.size
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.Layout
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.performClick
+import androidx.compose.ui.unit.dp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import androidx.test.filters.SdkSuppress
+import org.junit.Assert.assertEquals
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+class ClickNotPlacedChildTest {
+
+    @get:Rule
+    val composeTestRule = createComposeRule()
+
+    @Test
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+    fun childIsDisplayedWhenItWasNotPlacedOriginallyButPlacedLater() {
+        var firstClickedTimes = 0
+        var secondClickedTimes = 0
+        composeTestRule.setContent {
+            Layout(
+                content = {
+                    Box(
+                        Modifier.fillMaxSize().clickable {
+                            firstClickedTimes++
+                        }
+                    )
+                    Box(
+                        Modifier.fillMaxSize().clickable {
+                            secondClickedTimes++
+                        }
+                    )
+                },
+                modifier = Modifier.size(100.dp).testTag("parent")
+            ) { measutables, constraints ->
+                val first = measutables[0].measure(constraints)
+                measutables[1].measure(constraints)
+                layout(first.width, first.height) {
+                    first.place(0, 0)
+                }
+            }
+        }
+
+        composeTestRule.onNodeWithTag("parent")
+            .performClick()
+
+        composeTestRule.runOnIdle {
+            assertEquals(0, secondClickedTimes)
+            assertEquals(1, firstClickedTimes)
+        }
+    }
+}
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/PointerInputEventProcessorTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/PointerInputEventProcessorTest.kt
index 3ae5bed..26bf47f 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/PointerInputEventProcessorTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/PointerInputEventProcessorTest.kt
@@ -16,6 +16,7 @@
 
 package androidx.compose.ui.input.pointer
 
+import androidx.compose.foundation.layout.offset
 import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.autofill.Autofill
@@ -29,9 +30,10 @@
 import androidx.compose.ui.layout.Measurable
 import androidx.compose.ui.layout.MeasureResult
 import androidx.compose.ui.layout.MeasureScope
+import androidx.compose.ui.layout.layout
 import androidx.compose.ui.node.InternalCoreApi
 import androidx.compose.ui.node.LayoutNode
-import androidx.compose.ui.node.LayoutNodeWrapper
+import androidx.compose.ui.node.MeasureAndLayoutDelegate
 import androidx.compose.ui.node.OwnedLayer
 import androidx.compose.ui.node.Owner
 import androidx.compose.ui.node.OwnerSnapshotObserver
@@ -51,7 +53,6 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
 import com.google.common.truth.Truth.assertThat
-import com.nhaarman.mockitokotlin2.spy
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -83,15 +84,20 @@
 @RunWith(AndroidJUnit4::class)
 class PointerInputEventProcessorTest {
 
-    private lateinit var root: LayoutNode
     private lateinit var pointerInputEventProcessor: PointerInputEventProcessor
-    private val testOwner: TestOwner = spy()
+    private lateinit var testOwner: TestOwner
 
     @Before
     fun setup() {
-        root = LayoutNode(0, 0, 500, 500)
-        root.attach(testOwner)
-        pointerInputEventProcessor = PointerInputEventProcessor(root)
+        testOwner = TestOwner()
+        pointerInputEventProcessor = PointerInputEventProcessor(testOwner.root)
+    }
+
+    private fun addToRoot(vararg layoutNodes: LayoutNode) {
+        layoutNodes.forEachIndexed { index, node ->
+            testOwner.root.insertAt(index, node)
+        }
+        testOwner.measureAndLayout()
     }
 
     @Test
@@ -110,7 +116,7 @@
             )
         )
 
-        root.insertAt(0, layoutNode)
+        addToRoot(layoutNode)
 
         val offset = Offset(100f, 200f)
         val previousEvents = mutableListOf<PointerInputEventData>()
@@ -173,7 +179,7 @@
             )
         )
 
-        root.insertAt(0, layoutNode)
+        addToRoot(layoutNode)
 
         val offset = Offset(100f, 200f)
         val offset2 = Offset(300f, 400f)
@@ -230,7 +236,7 @@
             )
         )
 
-        root.insertAt(0, layoutNode)
+        addToRoot(layoutNode)
 
         val offsets = arrayOf(
             Offset(100f, 200f),
@@ -296,7 +302,7 @@
             )
         )
 
-        root.insertAt(0, layoutNode)
+        addToRoot(layoutNode)
 
         val offsets = arrayOf(
             Offset(99f, 200f),
@@ -372,7 +378,7 @@
             ).apply {
                 insertAt(0, middleLayoutNode)
             }
-        root.insertAt(0, parentLayoutNode)
+        addToRoot(parentLayoutNode)
 
         val offset = when (numberOfChildrenHit) {
             3 -> Offset(250f, 250f)
@@ -466,7 +472,7 @@
             )
         )
 
-        root.insertAt(0, layoutNode)
+        addToRoot(layoutNode)
 
         val down = PointerInputEvent(
             0,
@@ -587,7 +593,7 @@
 
         testOwner.position = IntOffset(aOX, aOY)
 
-        root.insertAt(0, parentLayoutNode)
+        addToRoot(parentLayoutNode)
 
         val additionalOffset = IntOffset(aOX, aOY)
 
@@ -752,10 +758,7 @@
                     childPointerInputFilter2
                 )
             )
-        root.apply {
-            insertAt(0, childLayoutNode1)
-            insertAt(0, childLayoutNode2)
-        }
+        addToRoot(childLayoutNode1, childLayoutNode2)
 
         val offset1 = Offset(25f, 25f)
         val offset2 = Offset(75f, 75f)
@@ -904,11 +907,7 @@
             )
         )
 
-        root.apply {
-            insertAt(0, childLayoutNode1)
-            insertAt(1, childLayoutNode2)
-            insertAt(2, childLayoutNode3)
-        }
+        addToRoot(childLayoutNode1, childLayoutNode2, childLayoutNode3)
 
         val offset1 = Offset(25f, 25f)
         val offset2 = Offset(75f, 75f)
@@ -1084,10 +1083,7 @@
             )
         )
 
-        root.apply {
-            insertAt(0, childLayoutNode1)
-            insertAt(1, childLayoutNode2)
-        }
+        addToRoot(childLayoutNode1, childLayoutNode2)
 
         val offset1 = Offset(50f, 25f)
         val offset2 = Offset(50f, 75f)
@@ -1204,10 +1200,7 @@
             )
         )
 
-        root.apply {
-            insertAt(0, childLayoutNode1)
-            insertAt(1, childLayoutNode2)
-        }
+        addToRoot(childLayoutNode1, childLayoutNode2)
 
         val offset1 = Offset(25f, 50f)
         val offset2 = Offset(75f, 50f)
@@ -1351,9 +1344,8 @@
             insertAt(2, layoutNodeBottomLeft)
             insertAt(3, layoutNodeBottomRight)
         }
-        root.apply {
-            insertAt(0, parentLayoutNode)
-        }
+        addToRoot(parentLayoutNode)
+
         val offsetsTopLeft =
             listOf(
                 Offset(0f, 1f),
@@ -1540,9 +1532,7 @@
                 singlePointerInputFilter
             )
         )
-        root.apply {
-            insertAt(0, layoutNode)
-        }
+        addToRoot(layoutNode)
         val offsetsThatHit =
             listOf(
                 Offset(2f, 2f),
@@ -1616,9 +1606,7 @@
             modifier
         )
 
-        root.apply {
-            insertAt(0, layoutNode)
-        }
+        addToRoot(layoutNode)
 
         val offset1 = Offset(50f, 75f)
 
@@ -1698,9 +1686,7 @@
         val layoutNode4: LayoutNode = LayoutNode(4, 8, 500, 500).apply {
             insertAt(0, layoutNode3)
         }
-        root.apply {
-            insertAt(0, layoutNode4)
-        }
+        addToRoot(layoutNode4)
 
         val offset1 = Offset(499f, 499f)
 
@@ -1776,9 +1762,7 @@
         val layoutNode5: LayoutNode = LayoutNode(5, 10, 500, 500).apply {
             insertAt(0, layoutNode4)
         }
-        root.apply {
-            insertAt(0, layoutNode5)
-        }
+        addToRoot(layoutNode5)
 
         val offset1 = Offset(499f, 499f)
 
@@ -1884,10 +1868,7 @@
             )
         )
 
-        root.apply {
-            insertAt(0, layoutNode1)
-            insertAt(1, layoutNode2)
-        }
+        addToRoot(layoutNode1, layoutNode2)
 
         val down = PointerInputEvent(
             1, 0, Offset(50f, 50f), true
@@ -1912,9 +1893,7 @@
             PointerInputModifierImpl2(pointerInputFilter1)
         )
 
-        root.apply {
-            insertAt(0, layoutNode1)
-        }
+        addToRoot(layoutNode1)
 
         val down = PointerInputEvent(
             1, 0, Offset(0f, 0f), true
@@ -1946,7 +1925,7 @@
             PointerInputModifierImpl2(pointerInputFilter)
         )
 
-        root.insertAt(0, layoutNode)
+        addToRoot(layoutNode)
 
         val pointerInputEvent =
             PointerInputEvent(
@@ -2005,7 +1984,7 @@
             )
         )
 
-        root.insertAt(0, layoutNode)
+        addToRoot(layoutNode)
 
         val pointerInputEvent1 =
             PointerInputEvent(
@@ -2123,8 +2102,7 @@
             PointerInputModifierImpl2(pointerInputFilter2)
         )
 
-        root.insertAt(0, layoutNode1)
-        root.insertAt(1, layoutNode2)
+        addToRoot(layoutNode1, layoutNode2)
 
         val pointerInputEventData1 =
             PointerInputEventData(
@@ -2217,7 +2195,7 @@
             PointerInputModifierImpl2(pointerInputFilter)
         )
 
-        root.insertAt(0, layoutNode)
+        addToRoot(layoutNode)
 
         val down =
             PointerInputEvent(
@@ -2304,7 +2282,7 @@
             PointerInputModifierImpl2(pointerInputFilter)
         )
 
-        root.insertAt(0, layoutNode)
+        addToRoot(layoutNode)
 
         val down =
             PointerInputEvent(
@@ -2364,7 +2342,7 @@
             )
         )
 
-        root.insertAt(0, layoutNode)
+        addToRoot(layoutNode)
 
         val down1 =
             PointerInputEvent(
@@ -2460,7 +2438,7 @@
             insertAt(0, childLayoutNode)
         }
 
-        root.insertAt(0, parentLayoutNode)
+        addToRoot(parentLayoutNode)
 
         val offset = Offset(50f, 50f)
 
@@ -2575,7 +2553,7 @@
             insertAt(0, childLayoutNode)
         }
 
-        root.insertAt(0, parentLayoutNode)
+        addToRoot(parentLayoutNode)
 
         val down =
             PointerInputEvent(0, 7, Offset(50f, 50f), true)
@@ -2616,7 +2594,7 @@
             insertAt(0, childLayoutNode)
         }
 
-        root.insertAt(0, parentLayoutNode)
+        addToRoot(parentLayoutNode)
 
         val offset = Offset(50f, 50f)
 
@@ -2731,7 +2709,7 @@
             insertAt(0, childLayoutNode)
         }
 
-        root.insertAt(0, parentLayoutNode)
+        addToRoot(parentLayoutNode)
 
         val down =
             PointerInputEvent(0, 7, Offset(50f, 50f), true)
@@ -2779,9 +2757,7 @@
             )
         )
 
-        root.apply {
-            insertAt(0, layoutNode)
-        }
+        addToRoot(layoutNode)
 
         val offsets =
             listOf(
@@ -2824,7 +2800,7 @@
                 pointerInputFilter
             )
         )
-        root.apply { insertAt(0, layoutNode) }
+        addToRoot(layoutNode)
         val pointerInputEvent =
             PointerInputEvent(0, 11, Offset(0f, 0f), true)
 
@@ -2854,14 +2830,14 @@
                 pointerInputFilter
             )
         )
-        root.apply { insertAt(0, layoutNode) }
+        addToRoot(layoutNode)
         val down = PointerInputEvent(0, 11, Offset(0f, 0f), true)
         pointerInputEventProcessor.process(down)
         val move = PointerInputEvent(0, 11, Offset(1f, 0f), true)
 
         // Act
 
-        root.removeAt(0, 1)
+        testOwner.root.removeAt(0, 1)
         val result = pointerInputEventProcessor.process(move)
 
         // Assert
@@ -2886,7 +2862,7 @@
                 pointerInputFilter
             )
         )
-        root.apply { insertAt(0, layoutNode) }
+        addToRoot(layoutNode)
         val down = PointerInputEvent(0, 11, Offset(0f, 0f), true)
         pointerInputEventProcessor.process(down)
         val move = PointerInputEvent(0, 11, Offset(1f, 0f), true)
@@ -2927,7 +2903,7 @@
                 pointerInputFilter
             )
         )
-        root.apply { insertAt(0, layoutNode) }
+        addToRoot(layoutNode)
         val down = PointerInputEvent(0, 11, Offset(0f, 0f), true)
         pointerInputEventProcessor.process(down)
         val move = PointerInputEvent(0, 11, Offset(1f, 0f), true)
@@ -2987,59 +2963,45 @@
     }
 }
 
-abstract class TestOwner : Owner {
-    var position: IntOffset? = null
-
-    override val root: LayoutNode
-        get() = LayoutNode()
-
-    override fun calculatePosition(): IntOffset {
-        return position ?: IntOffset.Zero
-    }
-}
-
 private class PointerInputModifierImpl2(override val pointerInputFilter: PointerInputFilter) :
     PointerInputModifier
 
 private fun LayoutNode(x: Int, y: Int, x2: Int, y2: Int, modifier: Modifier = Modifier) =
     LayoutNode().apply {
-        this.modifier = modifier
+        this.modifier = Modifier.layout { measurable, constraints ->
+            val placeable = measurable.measure(constraints)
+            layout(placeable.width, placeable.height) {
+                placeable.place(x, y)
+            }
+        }.then(modifier)
         measureBlocks = object : LayoutNode.NoIntrinsicsMeasureBlocks("not supported") {
             override fun measure(
                 measureScope: MeasureScope,
                 measurables: List<Measurable>,
                 constraints: Constraints
             ): MeasureResult =
-                measureScope.layout(x2 - x, y2 - y) {}
+                measureScope.layout(x2 - x, y2 - y) {
+                    measurables.forEach { it.measure(constraints).place(0, 0) }
+                }
         }
-        attach(mockOwner())
-        layoutState = LayoutNode.LayoutState.NeedsRemeasure
-        remeasure(Constraints())
-        var wrapper: LayoutNodeWrapper? = outerLayoutNodeWrapper
-        while (wrapper != null) {
-            wrapper.measureResult = innerLayoutNodeWrapper.measureResult
-            wrapper = (wrapper as? LayoutNodeWrapper)?.wrapped
-        }
-        place(x, y)
-        detach()
     }
 
-private fun mockOwner(
-    position: IntOffset = IntOffset.Zero,
-    targetRoot: LayoutNode = LayoutNode()
-): Owner = MockOwner(position, targetRoot)
-
 @OptIn(ExperimentalComposeUiApi::class, InternalCoreApi::class)
-private class MockOwner(
-    private val position: IntOffset,
-    private val targetRoot: LayoutNode
-) : Owner {
+private class TestOwner : Owner {
+    var position: IntOffset = IntOffset.Zero
+    override val root = LayoutNode(0, 0, 500, 500)
+
+    private val delegate = MeasureAndLayoutDelegate(root)
+
+    init {
+        root.attach(this)
+        delegate.updateRootConstraints(Constraints(maxWidth = 500, maxHeight = 500))
+    }
+
     override fun calculatePosition(): IntOffset = position
     override fun calculatePositionInWindow(): IntOffset = position
 
     override fun requestFocus(): Boolean = false
-    override val root: LayoutNode
-        get() = targetRoot
     override val rootForTest: RootForTest
         get() = TODO("Not yet implemented")
     override val hapticFeedBack: HapticFeedback
@@ -3069,9 +3031,11 @@
         set(@Suppress("UNUSED_PARAMETER") value) {}
 
     override fun onRequestMeasure(layoutNode: LayoutNode) {
+        delegate.requestRemeasure(layoutNode)
     }
 
     override fun onRequestRelayout(layoutNode: LayoutNode) {
+        delegate.requestRelayout(layoutNode)
     }
 
     override fun onAttach(node: LayoutNode) {
@@ -3081,6 +3045,7 @@
     }
 
     override fun measureAndLayout() {
+        delegate.measureAndLayout()
     }
 
     override fun createLayer(
diff --git a/compose/runtime/runtime-dispatch/src/androidAndroidTest/kotlin/androidx/compose/runtime/dispatch/AndroidUiDispatcherTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/platform/AndroidUiDispatcherTest.kt
similarity index 80%
rename from compose/runtime/runtime-dispatch/src/androidAndroidTest/kotlin/androidx/compose/runtime/dispatch/AndroidUiDispatcherTest.kt
rename to compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/platform/AndroidUiDispatcherTest.kt
index a46885d..50a283b 100644
--- a/compose/runtime/runtime-dispatch/src/androidAndroidTest/kotlin/androidx/compose/runtime/dispatch/AndroidUiDispatcherTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/platform/AndroidUiDispatcherTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Android Open Source Project
+ * Copyright 2021 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,13 +14,16 @@
  * limitations under the License.
  */
 
-package androidx.compose.runtime.dispatch
+package androidx.compose.ui.platform
 
 import android.graphics.Rect
 import android.view.Choreographer
 import android.view.MotionEvent
 import android.view.View
 import android.view.ViewGroup.LayoutParams
+import androidx.appcompat.app.AppCompatActivity
+import androidx.compose.runtime.MonotonicFrameClock
+import androidx.compose.runtime.withFrameNanos
 import androidx.core.view.doOnLayout
 import androidx.test.ext.junit.rules.activityScenarioRule
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -31,9 +34,10 @@
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.runBlocking
-import kotlinx.coroutines.withTimeout
+import kotlinx.coroutines.withTimeoutOrNull
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertNotEquals
+import org.junit.Assert.assertNotNull
 import org.junit.Assert.assertSame
 import org.junit.Assert.assertTrue
 import org.junit.Rule
@@ -44,7 +48,7 @@
 @RunWith(AndroidJUnit4::class)
 class AndroidUiDispatcherTest {
     @get:Rule
-    val rule = activityScenarioRule<TestActivity>()
+    val rule = activityScenarioRule<AppCompatActivity>()
 
     @Test
     fun currentThreadIsMainOnMainThread() = runBlocking(Dispatchers.Main) {
@@ -133,28 +137,32 @@
             // in the same frame if the resume was triggered by the input event.
             val rect = layoutRect.await()
             swipe(rect.left + 1, rect.top + 1, rect.right - 1, rect.bottom - 1, 30)
+            waitForIdle()
         }
 
-        withTimeout(3_000) {
-            val viewTouched = viewTouchedOnFrame.await()
-            val inputJob = ranInputJobOnFrame.await()
-            assertNotEquals(0, viewTouched)
-            assertNotEquals(0, inputJob)
-            assertEquals(
-                "touch and launched job resume happened on same frame",
-                viewTouched,
-                inputJob
-            )
-            assertEquals(
-                "withFrame ran on the same frame where it was called",
-                inputJob,
-                withFrameOnFrame.await()
-            )
-            assertEquals(
-                "second withFrame call was invoked on the very next frame",
-                inputJob + 1,
-                withFrameSecondCall.await()
-            )
-        }
+        assertNotNull(
+            "Timeout exceeded waiting for response to input events",
+            withTimeoutOrNull(5_000) {
+                val viewTouched = viewTouchedOnFrame.await()
+                val inputJob = ranInputJobOnFrame.await()
+                assertNotEquals(0, viewTouched)
+                assertNotEquals(0, inputJob)
+                assertEquals(
+                    "touch and launched job resume happened on same frame",
+                    viewTouched,
+                    inputJob
+                )
+                assertEquals(
+                    "withFrame ran on the same frame where it was called",
+                    inputJob,
+                    withFrameOnFrame.await()
+                )
+                assertEquals(
+                    "second withFrame call was invoked on the very next frame",
+                    inputJob + 1,
+                    withFrameSecondCall.await()
+                )
+            }
+        )
     }
 }
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/platform/WindowRecomposerTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/platform/WindowRecomposerTest.kt
index fd51969..26d6a75 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/platform/WindowRecomposerTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/platform/WindowRecomposerTest.kt
@@ -21,7 +21,6 @@
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.runtime.Recomposer
-import androidx.compose.runtime.dispatch.AndroidUiDispatcher
 import androidx.compose.ui.InternalComposeUiApi
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.background
diff --git a/compose/runtime/runtime-dispatch/src/androidMain/kotlin/androidx/compose/runtime/dispatch/AndroidUiDispatcher.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidUiDispatcher.kt
similarity index 95%
rename from compose/runtime/runtime-dispatch/src/androidMain/kotlin/androidx/compose/runtime/dispatch/AndroidUiDispatcher.kt
rename to compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidUiDispatcher.kt
index 2141b1d..e72b4a2 100644
--- a/compose/runtime/runtime-dispatch/src/androidMain/kotlin/androidx/compose/runtime/dispatch/AndroidUiDispatcher.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidUiDispatcher.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Android Open Source Project
+ * Copyright 2021 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,12 +14,13 @@
  * limitations under the License.
  */
 
-package androidx.compose.runtime.dispatch
+package androidx.compose.ui.platform
 
 import android.os.Looper
 import android.view.Choreographer
-import androidx.compose.runtime.dispatch.AndroidUiDispatcher.Companion.CurrentThread
-import androidx.compose.runtime.dispatch.AndroidUiDispatcher.Companion.Main
+import androidx.compose.runtime.MonotonicFrameClock
+import androidx.compose.ui.platform.AndroidUiDispatcher.Companion.CurrentThread
+import androidx.compose.ui.platform.AndroidUiDispatcher.Companion.Main
 import androidx.core.os.HandlerCompat
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.Dispatchers
diff --git a/compose/runtime/runtime-dispatch/src/androidMain/kotlin/androidx/compose/runtime/dispatch/AndroidUiFrameClock.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidUiFrameClock.kt
similarity index 93%
rename from compose/runtime/runtime-dispatch/src/androidMain/kotlin/androidx/compose/runtime/dispatch/AndroidUiFrameClock.kt
rename to compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidUiFrameClock.kt
index 71318f4..fbc7b21 100644
--- a/compose/runtime/runtime-dispatch/src/androidMain/kotlin/androidx/compose/runtime/dispatch/AndroidUiFrameClock.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidUiFrameClock.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Android Open Source Project
+ * Copyright 2021 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package androidx.compose.runtime.dispatch
+package androidx.compose.ui.platform
 
 import android.view.Choreographer
 import kotlinx.coroutines.suspendCancellableCoroutine
@@ -23,7 +23,7 @@
 
 class AndroidUiFrameClock(
     val choreographer: Choreographer
-) : MonotonicFrameClock {
+) : androidx.compose.runtime.MonotonicFrameClock {
     override suspend fun <R> withFrameNanos(
         onFrame: (Long) -> R
     ): R {
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/GlobalSnapshotManager.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/GlobalSnapshotManager.kt
index e869b2a..d702ebf 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/GlobalSnapshotManager.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/GlobalSnapshotManager.kt
@@ -17,7 +17,6 @@
 package androidx.compose.ui.platform
 
 import androidx.compose.runtime.ExperimentalComposeApi
-import androidx.compose.runtime.dispatch.AndroidUiDispatcher
 import androidx.compose.runtime.snapshots.Snapshot
 import androidx.compose.runtime.snapshots.SnapshotWriteObserver
 import androidx.compose.ui.platform.GlobalSnapshotManager.ensureStarted
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/WindowRecomposer.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/WindowRecomposer.kt
index b865bd9..c90661e 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/WindowRecomposer.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/WindowRecomposer.kt
@@ -21,8 +21,7 @@
 import androidx.compose.runtime.CompositionReference
 import androidx.compose.runtime.PausableMonotonicFrameClock
 import androidx.compose.runtime.Recomposer
-import androidx.compose.runtime.dispatch.AndroidUiDispatcher
-import androidx.compose.runtime.dispatch.MonotonicFrameClock
+import androidx.compose.runtime.MonotonicFrameClock
 import androidx.compose.ui.InternalComposeUiApi
 import androidx.compose.ui.R
 import androidx.lifecycle.Lifecycle
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/InnerPlaceable.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/InnerPlaceable.kt
index 0414d6a..f496339 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/InnerPlaceable.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/InnerPlaceable.kt
@@ -146,8 +146,12 @@
             // not add PointerInputFilters on different paths so we should not even go looking.
             val originalSize = hitPointerInputFilters.size
             layoutNode.zSortedChildren.reversedAny { child ->
-                callHitTest(child, pointerPositionRelativeToScreen, hitPointerInputFilters)
-                hitPointerInputFilters.size > originalSize
+                if (child.isPlaced) {
+                    callHitTest(child, pointerPositionRelativeToScreen, hitPointerInputFilters)
+                    hitPointerInputFilters.size > originalSize
+                } else {
+                    false
+                }
             }
         }
     }
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/Wrapper.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/Wrapper.kt
index 3cdf0e5..772faee 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/Wrapper.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/Wrapper.kt
@@ -18,23 +18,37 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Composition
 import androidx.compose.runtime.CompositionReference
-import androidx.compose.runtime.EmbeddingContext
+import androidx.compose.runtime.DefaultMonotonicFrameClock
 import androidx.compose.runtime.ExperimentalComposeApi
 import androidx.compose.runtime.Providers
 import androidx.compose.runtime.Recomposer
 import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.node.LayoutNode
+import kotlin.coroutines.CoroutineContext
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.CoroutineStart
+import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.NonCancellable
 import kotlinx.coroutines.launch
+import kotlinx.coroutines.swing.Swing
+import javax.swing.SwingUtilities
+
+object SwingEmbeddingContext {
+    fun isMainThread(): Boolean {
+        return SwingUtilities.isEventDispatchThread()
+    }
+
+    fun mainThreadCompositionContext(): CoroutineContext {
+        return Dispatchers.Swing + DefaultMonotonicFrameClock
+    }
+}
 
 // TODO: Replace usages with an appropriately scoped implementation
 // Below is a local copy of the old Recomposer.current() implementation.
 @OptIn(ExperimentalCoroutinesApi::class)
 private val GlobalDefaultRecomposer = run {
-    val embeddingContext = EmbeddingContext()
+    val embeddingContext = SwingEmbeddingContext
     val mainScope = CoroutineScope(
         NonCancellable + embeddingContext.mainThreadCompositionContext()
     )
diff --git a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/ComposedModifierTest.kt b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/ComposedModifierTest.kt
index 679e9e9..417f745 100644
--- a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/ComposedModifierTest.kt
+++ b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/ComposedModifierTest.kt
@@ -25,7 +25,7 @@
 import androidx.compose.runtime.RecomposeScope
 import androidx.compose.runtime.currentComposer
 import androidx.compose.runtime.currentRecomposeScope
-import androidx.compose.runtime.dispatch.MonotonicFrameClock
+import androidx.compose.runtime.MonotonicFrameClock
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.withRunningRecomposer
 import kotlinx.coroutines.channels.Channel
diff --git a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/node/LayoutNodeTest.kt b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/node/LayoutNodeTest.kt
index 4f34aef..e31400b8 100644
--- a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/node/LayoutNodeTest.kt
+++ b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/node/LayoutNodeTest.kt
@@ -32,6 +32,8 @@
 import androidx.compose.ui.graphics.graphicsLayer
 import androidx.compose.ui.hapticfeedback.HapticFeedback
 import androidx.compose.ui.input.key.KeyEvent
+import androidx.compose.ui.input.pointer.PointerEvent
+import androidx.compose.ui.input.pointer.PointerEventPass
 import androidx.compose.ui.input.pointer.PointerInputFilter
 import androidx.compose.ui.input.pointer.PointerInputModifier
 import androidx.compose.ui.layout.LayoutModifier
@@ -54,7 +56,6 @@
 import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.zIndex
 import com.google.common.truth.Truth.assertThat
-import com.nhaarman.mockitokotlin2.spy
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertFalse
 import org.junit.Assert.assertNull
@@ -794,7 +795,7 @@
 
     @Test
     fun hitTest_pointerInBounds_pointerInputFilterHit() {
-        val pointerInputFilter: PointerInputFilter = spy()
+        val pointerInputFilter: PointerInputFilter = mockPointerInputFilter()
         val layoutNode =
             LayoutNode(
                 0, 0, 1, 1,
@@ -811,7 +812,7 @@
 
     @Test
     fun hitTest_pointerOutOfBounds_nothingHit() {
-        val pointerInputFilter: PointerInputFilter = spy()
+        val pointerInputFilter: PointerInputFilter = mockPointerInputFilter()
         val layoutNode =
             LayoutNode(
                 0, 0, 1, 1,
@@ -854,9 +855,9 @@
     private fun hitTest_nestedOffsetNodes_allHitInCorrectOrder(numberOfChildrenHit: Int) {
         // Arrange
 
-        val childPointerInputFilter: PointerInputFilter = spy()
-        val middlePointerInputFilter: PointerInputFilter = spy()
-        val parentPointerInputFilter: PointerInputFilter = spy()
+        val childPointerInputFilter: PointerInputFilter = mockPointerInputFilter()
+        val middlePointerInputFilter: PointerInputFilter = mockPointerInputFilter()
+        val parentPointerInputFilter: PointerInputFilter = mockPointerInputFilter()
 
         val childLayoutNode =
             LayoutNode(
@@ -884,6 +885,8 @@
                 insertAt(0, middleLayoutNode)
                 attach(MockOwner())
             }
+        middleLayoutNode.onNodePlaced()
+        childLayoutNode.onNodePlaced()
 
         val offset = when (numberOfChildrenHit) {
             3 -> Offset(250f, 250f)
@@ -952,8 +955,8 @@
 
         // Arrange
 
-        val childPointerInputFilter1: PointerInputFilter = spy()
-        val childPointerInputFilter2: PointerInputFilter = spy()
+        val childPointerInputFilter1: PointerInputFilter = mockPointerInputFilter()
+        val childPointerInputFilter2: PointerInputFilter = mockPointerInputFilter()
 
         val childLayoutNode1 =
             LayoutNode(
@@ -976,6 +979,8 @@
             insertAt(1, childLayoutNode2)
             attach(MockOwner())
         }
+        childLayoutNode1.onNodePlaced()
+        childLayoutNode2.onNodePlaced()
 
         val offset1 = Offset(25f, 25f)
         val offset2 = Offset(75f, 75f)
@@ -1019,9 +1024,9 @@
     @Test
     fun hitTest_3DownOnOverlappingPointerInputModifiers_resultIsCorrect() {
 
-        val childPointerInputFilter1: PointerInputFilter = spy()
-        val childPointerInputFilter2: PointerInputFilter = spy()
-        val childPointerInputFilter3: PointerInputFilter = spy()
+        val childPointerInputFilter1: PointerInputFilter = mockPointerInputFilter()
+        val childPointerInputFilter2: PointerInputFilter = mockPointerInputFilter()
+        val childPointerInputFilter3: PointerInputFilter = mockPointerInputFilter()
 
         val childLayoutNode1 =
             LayoutNode(
@@ -1053,6 +1058,9 @@
             insertAt(2, childLayoutNode3)
             attach(MockOwner())
         }
+        childLayoutNode1.onNodePlaced()
+        childLayoutNode2.onNodePlaced()
+        childLayoutNode3.onNodePlaced()
 
         val offset1 = Offset(25f, 25f)
         val offset2 = Offset(75f, 75f)
@@ -1094,8 +1102,8 @@
     @Test
     fun hitTest_3DownOnFloatingPointerInputModifierV_resultIsCorrect() {
 
-        val childPointerInputFilter1: PointerInputFilter = spy()
-        val childPointerInputFilter2: PointerInputFilter = spy()
+        val childPointerInputFilter1: PointerInputFilter = mockPointerInputFilter()
+        val childPointerInputFilter2: PointerInputFilter = mockPointerInputFilter()
 
         val childLayoutNode1 = LayoutNode(
             0, 0, 100, 150,
@@ -1115,6 +1123,8 @@
             insertAt(1, childLayoutNode2)
             attach(MockOwner())
         }
+        childLayoutNode1.onNodePlaced()
+        childLayoutNode2.onNodePlaced()
 
         val offset1 = Offset(50f, 25f)
         val offset2 = Offset(50f, 75f)
@@ -1156,8 +1166,8 @@
     @Test
     fun hitTest_3DownOnFloatingPointerInputModifierH_resultIsCorrect() {
 
-        val childPointerInputFilter1: PointerInputFilter = spy()
-        val childPointerInputFilter2: PointerInputFilter = spy()
+        val childPointerInputFilter1: PointerInputFilter = mockPointerInputFilter()
+        val childPointerInputFilter2: PointerInputFilter = mockPointerInputFilter()
 
         val childLayoutNode1 = LayoutNode(
             0, 0, 150, 100,
@@ -1177,6 +1187,8 @@
             insertAt(1, childLayoutNode2)
             attach(MockOwner())
         }
+        childLayoutNode2.onNodePlaced()
+        childLayoutNode1.onNodePlaced()
 
         val offset1 = Offset(25f, 50f)
         val offset2 = Offset(75f, 50f)
@@ -1226,10 +1238,10 @@
 
         // Arrange
 
-        val pointerInputFilter1: PointerInputFilter = spy()
-        val pointerInputFilter2: PointerInputFilter = spy()
-        val pointerInputFilter3: PointerInputFilter = spy()
-        val pointerInputFilter4: PointerInputFilter = spy()
+        val pointerInputFilter1: PointerInputFilter = mockPointerInputFilter()
+        val pointerInputFilter2: PointerInputFilter = mockPointerInputFilter()
+        val pointerInputFilter3: PointerInputFilter = mockPointerInputFilter()
+        val pointerInputFilter4: PointerInputFilter = mockPointerInputFilter()
 
         val layoutNode1 = LayoutNode(
             -1, -1, 1, 1,
@@ -1263,6 +1275,10 @@
             insertAt(3, layoutNode4)
             attach(MockOwner())
         }
+        layoutNode1.onNodePlaced()
+        layoutNode2.onNodePlaced()
+        layoutNode3.onNodePlaced()
+        layoutNode4.onNodePlaced()
 
         val offsetsThatHit1 =
             listOf(
@@ -1346,7 +1362,7 @@
 
         // Arrange
 
-        val pointerInputFilter: PointerInputFilter = spy()
+        val pointerInputFilter: PointerInputFilter = mockPointerInputFilter()
 
         val layoutNode = LayoutNode(
             0, 0, 2, 2,
@@ -1396,9 +1412,9 @@
 
         // Arrange.
 
-        val pointerInputFilter1: PointerInputFilter = spy()
-        val pointerInputFilter2: PointerInputFilter = spy()
-        val pointerInputFilter3: PointerInputFilter = spy()
+        val pointerInputFilter1: PointerInputFilter = mockPointerInputFilter()
+        val pointerInputFilter2: PointerInputFilter = mockPointerInputFilter()
+        val pointerInputFilter3: PointerInputFilter = mockPointerInputFilter()
 
         val modifier =
             PointerInputModifierImpl(
@@ -1440,7 +1456,7 @@
 
         // Arrange.
 
-        val pointerInputFilter: PointerInputFilter = spy()
+        val pointerInputFilter: PointerInputFilter = mockPointerInputFilter()
 
         val layoutNode1 =
             LayoutNode(
@@ -1460,6 +1476,9 @@
         }.apply {
             attach(MockOwner())
         }
+        layoutNode3.onNodePlaced()
+        layoutNode2.onNodePlaced()
+        layoutNode1.onNodePlaced()
         val offset1 = Offset(499f, 499f)
 
         val hit = mutableListOf<PointerInputFilter>()
@@ -1478,10 +1497,10 @@
 
         // Arrange.
 
-        val pointerInputFilter1: PointerInputFilter = spy()
-        val pointerInputFilter2: PointerInputFilter = spy()
-        val pointerInputFilter3: PointerInputFilter = spy()
-        val pointerInputFilter4: PointerInputFilter = spy()
+        val pointerInputFilter1: PointerInputFilter = mockPointerInputFilter()
+        val pointerInputFilter2: PointerInputFilter = mockPointerInputFilter()
+        val pointerInputFilter3: PointerInputFilter = mockPointerInputFilter()
+        val pointerInputFilter4: PointerInputFilter = mockPointerInputFilter()
 
         val layoutNode1 = LayoutNode(
             1, 6, 500, 500,
@@ -1514,6 +1533,10 @@
         }.apply {
             attach(MockOwner())
         }
+        layoutNode4.onNodePlaced()
+        layoutNode3.onNodePlaced()
+        layoutNode2.onNodePlaced()
+        layoutNode1.onNodePlaced()
 
         val offset1 = Offset(499f, 499f)
 
@@ -1538,8 +1561,8 @@
     @Test
     fun hitTest_pointerOnFullyOverlappingPointerInputModifiers_onlyTopPimIsHit() {
 
-        val pointerInputFilter1: PointerInputFilter = spy()
-        val pointerInputFilter2: PointerInputFilter = spy()
+        val pointerInputFilter1: PointerInputFilter = mockPointerInputFilter()
+        val pointerInputFilter2: PointerInputFilter = mockPointerInputFilter()
 
         val layoutNode1 = LayoutNode(
             0, 0, 100, 100,
@@ -1559,6 +1582,8 @@
             insertAt(1, layoutNode2)
             attach(MockOwner())
         }
+        layoutNode1.onNodePlaced()
+        layoutNode2.onNodePlaced()
 
         val offset = Offset(50f, 50f)
 
@@ -1576,7 +1601,7 @@
     @Test
     fun hitTest_pointerOnPointerInputModifierInLayoutNodeWithNoSize_nothingHit() {
 
-        val pointerInputFilter: PointerInputFilter = spy()
+        val pointerInputFilter: PointerInputFilter = mockPointerInputFilter()
 
         val layoutNode = LayoutNode(
             0, 0, 0, 0,
@@ -1603,8 +1628,8 @@
     @Test
     fun hitTest_zIndexIsAccounted() {
 
-        val pointerInputFilter1: PointerInputFilter = spy()
-        val pointerInputFilter2: PointerInputFilter = spy()
+        val pointerInputFilter1: PointerInputFilter = mockPointerInputFilter()
+        val pointerInputFilter2: PointerInputFilter = mockPointerInputFilter()
 
         val parent = LayoutNode(
             0, 0, 2, 2
@@ -1869,7 +1894,7 @@
         get() = TODO("Not yet implemented")
 }
 
-fun LayoutNode(x: Int, y: Int, x2: Int, y2: Int, modifier: Modifier = Modifier) =
+private fun LayoutNode(x: Int, y: Int, x2: Int, y2: Int, modifier: Modifier = Modifier) =
     LayoutNode().apply {
         this.modifier = modifier
         measureBlocks = object : LayoutNode.NoIntrinsicsMeasureBlocks("not supported") {
@@ -1893,3 +1918,15 @@
         place(x, y)
         detach()
     }
+
+private fun mockPointerInputFilter(): PointerInputFilter = object : PointerInputFilter() {
+    override fun onPointerEvent(
+        pointerEvent: PointerEvent,
+        pass: PointerEventPass,
+        bounds: IntSize
+    ) {
+    }
+
+    override fun onCancel() {
+    }
+}
\ No newline at end of file
diff --git a/development/project-creator/create_project.py b/development/project-creator/create_project.py
index dc331f8..8ad4d34 100755
--- a/development/project-creator/create_project.py
+++ b/development/project-creator/create_project.py
@@ -77,7 +77,7 @@
         os.remove(path)
 
 def mv_dir(src_path_dir, dst_path_dir):
-    """Moves a direcotry from src_path_dir to dst_path_dir.
+    """Moves a directory from src_path_dir to dst_path_dir.
 
     Args:
         src_path_dir: the source directory, which must exist
@@ -87,16 +87,15 @@
         print_e('rename error: Destination path %s already exists.' % dst_path_dir)
         return None
     # If moving to a new parent directory, create that directory
-    final_dir_length = len("/" + dst_path_dir.split('/')[-1])
-    parent_dst_path_dir = dst_path_dir[:-final_dir_length]
+    parent_dst_path_dir = os.path.dirname(dst_path_dir)
     if not os.path.exists(parent_dst_path_dir):
         os.makedirs(parent_dst_path_dir)
     if not os.path.exists(src_path_dir):
         print_e('mv error: Source path %s does not exist.' % src_path_dir)
         return None
-    try : 
-        os.rename(src_path_dir, dst_path_dir)  
-    except OSError as error: 
+    try:
+        os.rename(src_path_dir, dst_path_dir)
+    except OSError as error:
         print_e('FAIL: Unable to copy %s to destination %s' % (src_path_dir, dst_path_dir))
         print_e(error)
         return None
@@ -234,12 +233,12 @@
     sed("<YEAR>", year, full_artifact_path + "/build.gradle")
     sed("<YEAR>", year, full_artifact_path + "/src/androidTest/AndroidManifest.xml")
     sed("<YEAR>", year, full_artifact_path + "/src/main/AndroidManifest.xml")
-    sed("0000", year, full_package_info_path)
+    sed("<YEAR>", year, full_package_info_path)
     # Populate the PACKAGE
     package = generate_package_name(group_id, artifact_id)
     sed("<PACKAGE>", package, full_artifact_path + "/src/androidTest/AndroidManifest.xml")
     sed("<PACKAGE>", package, full_artifact_path + "/src/main/AndroidManifest.xml")
-    sed("placeholder.package", package, full_package_info_path)
+    sed("<PACKAGE>", package, full_package_info_path)
     # Populate the VERSION macro
     group_id_version_macro = get_group_id_version_macro(group_id)
     sed("<GROUPID>", group_id_version_macro, full_artifact_path + "/build.gradle")
diff --git a/development/project-creator/groupId/artifactId/src/main/groupId/package-info.java b/development/project-creator/groupId/artifactId/src/main/groupId/package-info.java
index 708f84a..a51e68c 100644
--- a/development/project-creator/groupId/artifactId/src/main/groupId/package-info.java
+++ b/development/project-creator/groupId/artifactId/src/main/groupId/package-info.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 0000 The Android Open Source Project
+ * Copyright (C) <YEAR> 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.
@@ -17,4 +17,4 @@
 /**
  * Insert package level documentation here
  */
-package placeholder.package;
+package <PACKAGE>;
diff --git a/docs/api_guidelines.md b/docs/api_guidelines.md
index 9b1fcb7..fd7b5bb 100644
--- a/docs/api_guidelines.md
+++ b/docs/api_guidelines.md
@@ -60,19 +60,20 @@
 scenario:
 
 *   `androidx.library:1.0.0`
-    *   contains classes `androidx.library.A` and `androidx.library.util.B`
+    *   contains class `androidx.library.A`
+    *   contains class `androidx.library.util.B`
 
 This module is split, moving `androidx.library.util.B` to a new module:
 
 *   `androidx.library:1.1.0`
     *   contains class `androidx.library.A`
-    *   depends on `androidx.library.util:1.0.0`
-*   `androidx.library.util:1.0.0`
-    *   depends on `androidx.library.util.B`
+    *   depends on `androidx.library.util:1.1.0`
+*   `androidx.library.util:1.1.0`
+    *   contains class `androidx.library.util.B`
 
-A developer writes an app that depends directly on `androidx.library.util:1.0.0`
-and transitively pulls in `androidx.library:1.0.0`. Their app will no longer
-compile due to class duplication of `androidx.library.util.B`.
+A developer writes an app that depends directly on `androidx.library.util:1.1.0`
+and also transitively pulls in `androidx.library:1.0.0`. Their app will no
+longer compile due to class duplication of `androidx.library.util.B`.
 
 While it is possible for the developer to fix this by manually specifying a
 dependency on `androidx.library:1.1.0`, there is no easy way for the developer
@@ -960,9 +961,16 @@
 
 ### Kotlin {#dependencies-kotlin}
 
-Kotlin is _recommended_ for new libraries; however, it's important to consider
-its size impact on clients. Currently, the Kotlin stdlib adds a minimum of 40kB
-post-optimization.
+Kotlin is _strongly recommended_ for new libraries; however, it's important to
+consider its size impact on clients. Currently, the Kotlin stdlib adds a minimum
+of 40kB post-optimization. It may not make sense to use Kotlin for a library
+that targets Java-only clients or space-constrained (ex. Android Go) clients.
+
+Existing Java-based libraries are _strongly discouraged_ from using Kotlin,
+primarily because our documentation system does not currently provide a
+Java-facing version of Kotlin API reference docs. Java-based libraries _may_
+migrate to Kotlin, but they must consider the docs usability and size impacts on
+existing Java-only and space-constrained clients.
 
 ### Kotlin coroutines {#dependencies-coroutines}
 
diff --git a/docs/policies.md b/docs/policies.md
index f42d6dc..0b790f5 100644
--- a/docs/policies.md
+++ b/docs/policies.md
@@ -21,10 +21,10 @@
 ```
 <feature-name>/
   <feature-name>-<sub-feature>/ [<feature-name>:<feature-name>-<sub-feature>]
+    samples/ [<feature-name>:<feature-name>-<sub-feature>:samples]
   integration-tests/
     testapp/ [<feature-name>:testapp]
     testlib/ [<feature-name>:testlib]
-    samples/ [<feature-name>:samples]
 ```
 
 For example, the `navigation` library group's directory structure is:
diff --git a/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/DexInspectorTask.kt b/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/DexInspectorTask.kt
index b10b84b..4fa04be 100644
--- a/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/DexInspectorTask.kt
+++ b/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/DexInspectorTask.kt
@@ -73,12 +73,14 @@
 fun Project.registerDexInspectorTask(
     variant: BaseVariant,
     extension: BaseExtension,
+    jarName: String?,
     jar: TaskProvider<out Jar>
 ): TaskProvider<DexInspectorTask> {
     return tasks.register(variant.taskName("dexInspector"), DexInspectorTask::class.java) {
         it.setDx(extension.sdkDirectory, extension.buildToolsVersion)
         it.jars.from(jar.get().destinationDirectory)
-        val out = File(taskWorkingDir(variant, "dexedInspector"), "${project.name}.jar")
+        val name = jarName ?: "${project.name}.jar"
+        val out = File(taskWorkingDir(variant, "dexedInspector"), name)
         it.outputFile.set(out)
         it.dependsOn(jar)
     }
diff --git a/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/InspectionPlugin.kt b/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/InspectionPlugin.kt
index 81df245..9ee34da 100644
--- a/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/InspectionPlugin.kt
+++ b/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/InspectionPlugin.kt
@@ -27,6 +27,7 @@
 import org.gradle.api.tasks.StopExecutionException
 import org.gradle.api.tasks.TaskProvider
 import org.gradle.kotlin.dsl.apply
+import org.gradle.kotlin.dsl.create
 import org.gradle.kotlin.dsl.dependencies
 import org.gradle.kotlin.dsl.getPlugin
 import java.io.File
@@ -44,6 +45,7 @@
     override fun apply(project: Project) {
         var foundLibraryPlugin = false
         var foundReleaseVariant = false
+        val extension = project.extensions.create<InspectionExtension>(EXTENSION_NAME, project)
         project.pluginManager.withPlugin("com.android.library") {
             foundLibraryPlugin = true
             val libExtension = project.extensions.getByType(LibraryExtension::class.java)
@@ -53,7 +55,9 @@
                     foundReleaseVariant = true
                     val unzip = project.registerUnzipTask(variant)
                     val shadowJar = project.registerShadowDependenciesTask(variant, unzip)
-                    dexTask = project.registerDexInspectorTask(variant, libExtension, shadowJar)
+                    dexTask = project.registerDexInspectorTask(
+                        variant, libExtension, extension.name, shadowJar
+                    )
                 }
             }
             libExtension.sourceSets.findByName("main")!!.resources.srcDirs(
@@ -141,4 +145,13 @@
     libExtension.libraryVariants.all { variant ->
         libraryProject.registerGenerateProguardDetectionFileTask(variant)
     }
+}
+
+const val EXTENSION_NAME = "inspection"
+
+open class InspectionExtension(@Suppress("UNUSED_PARAMETER") project: Project) {
+    /**
+     * Name of built inspector artifact, if not provided it is equal to project's name.
+     */
+    var name: String? = null
 }
\ No newline at end of file
diff --git a/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/ShadowDependenciesTask.kt b/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/ShadowDependenciesTask.kt
index 853628b..9f1a0fb 100644
--- a/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/ShadowDependenciesTask.kt
+++ b/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/ShadowDependenciesTask.kt
@@ -40,6 +40,12 @@
         val fileTree = project.fileTree(zipTask.get().destinationDir)
         fileTree.include("**/*.jar", "**/*.so")
         it.from(fileTree)
+        it.includeEmptyDirs = false
+        it.filesMatching("**/*.so") {
+            if (it.path.startsWith("jni")) {
+                it.path = "lib/${it.path.removePrefix("jni")}"
+            }
+        }
         it.destinationDirectory.set(taskWorkingDir(variant, "shadowedJar"))
         it.archiveBaseName.set("${project.name}-shadowed")
         it.dependsOn(zipTask)
diff --git a/navigation/navigation-runtime/src/main/java/androidx/navigation/NavControllerViewModel.java b/navigation/navigation-runtime/src/main/java/androidx/navigation/NavControllerViewModel.java
deleted file mode 100644
index 84628e6..0000000
--- a/navigation/navigation-runtime/src/main/java/androidx/navigation/NavControllerViewModel.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.navigation;
-
-import androidx.annotation.NonNull;
-import androidx.lifecycle.ViewModel;
-import androidx.lifecycle.ViewModelProvider;
-import androidx.lifecycle.ViewModelStore;
-
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.UUID;
-
-/**
- * NavControllerViewModel is the always up to date view of the NavController's
- * non configuration state
- */
-class NavControllerViewModel extends ViewModel {
-
-    private static final ViewModelProvider.Factory FACTORY = new ViewModelProvider.Factory() {
-        @NonNull
-        @Override
-        @SuppressWarnings("unchecked")
-        public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
-            NavControllerViewModel viewModel = new NavControllerViewModel();
-            return (T) viewModel;
-        }
-    };
-
-    @NonNull
-    static NavControllerViewModel getInstance(ViewModelStore viewModelStore) {
-        ViewModelProvider viewModelProvider = new ViewModelProvider(viewModelStore, FACTORY);
-        return viewModelProvider.get(NavControllerViewModel.class);
-    }
-
-    private final HashMap<UUID, ViewModelStore> mViewModelStores = new HashMap<>();
-
-    void clear(@NonNull UUID backStackEntryUUID) {
-        // Clear and remove the NavGraph's ViewModelStore
-        ViewModelStore viewModelStore = mViewModelStores.remove(backStackEntryUUID);
-        if (viewModelStore != null) {
-            viewModelStore.clear();
-        }
-    }
-
-    @Override
-    protected void onCleared() {
-        for (ViewModelStore store: mViewModelStores.values()) {
-            store.clear();
-        }
-        mViewModelStores.clear();
-    }
-
-    @NonNull
-    ViewModelStore getViewModelStore(@NonNull UUID backStackEntryUUID) {
-        ViewModelStore viewModelStore = mViewModelStores.get(backStackEntryUUID);
-        if (viewModelStore == null) {
-            viewModelStore = new ViewModelStore();
-            mViewModelStores.put(backStackEntryUUID, viewModelStore);
-        }
-        return viewModelStore;
-    }
-
-    @NonNull
-    @Override
-    public String toString() {
-        StringBuilder sb = new StringBuilder("NavControllerViewModel{");
-        sb.append(Integer.toHexString(System.identityHashCode(this)));
-        sb.append("} ViewModelStores (");
-        Iterator<UUID> viewModelStoreIterator = mViewModelStores.keySet().iterator();
-        while (viewModelStoreIterator.hasNext()) {
-            sb.append(viewModelStoreIterator.next());
-            if (viewModelStoreIterator.hasNext()) {
-                sb.append(", ");
-            }
-        }
-        sb.append(')');
-        return sb.toString();
-    }
-}
diff --git a/navigation/navigation-runtime/src/main/java/androidx/navigation/NavControllerViewModel.kt b/navigation/navigation-runtime/src/main/java/androidx/navigation/NavControllerViewModel.kt
new file mode 100644
index 0000000..01667f6
--- /dev/null
+++ b/navigation/navigation-runtime/src/main/java/androidx/navigation/NavControllerViewModel.kt
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.navigation
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import androidx.lifecycle.ViewModelStore
+import androidx.lifecycle.get
+import java.util.UUID
+
+/**
+ * NavControllerViewModel is the always up to date view of the NavController's
+ * non configuration state
+ */
+internal class NavControllerViewModel : ViewModel() {
+    private val viewModelStores = mutableMapOf<UUID, ViewModelStore>()
+
+    fun clear(backStackEntryUUID: UUID) {
+        // Clear and remove the NavGraph's ViewModelStore
+        val viewModelStore = viewModelStores.remove(backStackEntryUUID)
+        viewModelStore?.clear()
+    }
+
+    override fun onCleared() {
+        for (store in viewModelStores.values) {
+            store.clear()
+        }
+        viewModelStores.clear()
+    }
+
+    fun getViewModelStore(backStackEntryUUID: UUID): ViewModelStore {
+        var viewModelStore = viewModelStores[backStackEntryUUID]
+        if (viewModelStore == null) {
+            viewModelStore = ViewModelStore()
+            viewModelStores[backStackEntryUUID] = viewModelStore
+        }
+        return viewModelStore
+    }
+
+    override fun toString(): String {
+        val sb = StringBuilder("NavControllerViewModel{")
+        sb.append(Integer.toHexString(System.identityHashCode(this)))
+        sb.append("} ViewModelStores (")
+        val viewModelStoreIterator: Iterator<UUID> = viewModelStores.keys.iterator()
+        while (viewModelStoreIterator.hasNext()) {
+            sb.append(viewModelStoreIterator.next())
+            if (viewModelStoreIterator.hasNext()) {
+                sb.append(", ")
+            }
+        }
+        sb.append(')')
+        return sb.toString()
+    }
+
+    companion object {
+        private val FACTORY: ViewModelProvider.Factory = object : ViewModelProvider.Factory {
+            @Suppress("UNCHECKED_CAST")
+            override fun <T : ViewModel?> create(modelClass: Class<T>): T {
+                return NavControllerViewModel() as T
+            }
+        }
+
+        @JvmStatic
+        fun getInstance(viewModelStore: ViewModelStore): NavControllerViewModel {
+            val viewModelProvider = ViewModelProvider(viewModelStore, FACTORY)
+            return viewModelProvider.get()
+        }
+    }
+}
diff --git a/navigation/navigation-ui/src/main/java/androidx/navigation/ui/ActionBarOnDestinationChangedListener.java b/navigation/navigation-ui/src/main/java/androidx/navigation/ui/ActionBarOnDestinationChangedListener.java
deleted file mode 100644
index 18fce74..0000000
--- a/navigation/navigation-ui/src/main/java/androidx/navigation/ui/ActionBarOnDestinationChangedListener.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.navigation.ui;
-
-import android.graphics.drawable.Drawable;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.RestrictTo;
-import androidx.annotation.StringRes;
-import androidx.appcompat.app.ActionBar;
-import androidx.appcompat.app.ActionBarDrawerToggle;
-import androidx.appcompat.app.AppCompatActivity;
-
-/**
- * The OnDestinationChangedListener specifically for keeping the ActionBar updated.
- * This handles both updating the title and updating the Up Indicator, transitioning between
- * the drawer icon and up arrow as needed.
- * @hide
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY)
-class ActionBarOnDestinationChangedListener extends
-        AbstractAppBarOnDestinationChangedListener {
-    private final AppCompatActivity mActivity;
-
-    ActionBarOnDestinationChangedListener(@NonNull AppCompatActivity activity,
-            @NonNull AppBarConfiguration configuration) {
-        super(activity.getDrawerToggleDelegate().getActionBarThemedContext(), configuration);
-        mActivity = activity;
-    }
-
-    @Override
-    protected void setTitle(CharSequence title) {
-        ActionBar actionBar = mActivity.getSupportActionBar();
-        actionBar.setTitle(title);
-    }
-
-    @Override
-    protected void setNavigationIcon(Drawable icon,
-            @StringRes int contentDescription) {
-        ActionBar actionBar = mActivity.getSupportActionBar();
-        if (icon == null) {
-            actionBar.setDisplayHomeAsUpEnabled(false);
-        } else {
-            actionBar.setDisplayHomeAsUpEnabled(true);
-            ActionBarDrawerToggle.Delegate delegate = mActivity.getDrawerToggleDelegate();
-            delegate.setActionBarUpIndicator(icon, contentDescription);
-        }
-    }
-}
diff --git a/navigation/navigation-ui/src/main/java/androidx/navigation/ui/ActionBarOnDestinationChangedListener.kt b/navigation/navigation-ui/src/main/java/androidx/navigation/ui/ActionBarOnDestinationChangedListener.kt
new file mode 100644
index 0000000..c23f619
--- /dev/null
+++ b/navigation/navigation-ui/src/main/java/androidx/navigation/ui/ActionBarOnDestinationChangedListener.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.navigation.ui
+
+import android.graphics.drawable.Drawable
+import androidx.annotation.RestrictTo
+import androidx.annotation.StringRes
+import androidx.appcompat.app.AppCompatActivity
+
+/**
+ * The OnDestinationChangedListener specifically for keeping the ActionBar updated.
+ * This handles both updating the title and updating the Up Indicator, transitioning between
+ * the drawer icon and up arrow as needed.
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+internal class ActionBarOnDestinationChangedListener(
+    private val activity: AppCompatActivity,
+    configuration: AppBarConfiguration
+) : AbstractAppBarOnDestinationChangedListener(
+    activity.drawerToggleDelegate!!.actionBarThemedContext,
+    configuration
+) {
+    override fun setTitle(title: CharSequence) {
+        val actionBar = activity.supportActionBar
+        require(actionBar != null) {
+            "Activity $activity does not have an ActionBar set via setSupportActionBar()"
+        }
+        actionBar.setTitle(title)
+    }
+
+    override fun setNavigationIcon(icon: Drawable, @StringRes contentDescription: Int) {
+        val actionBar = activity.supportActionBar
+        require(actionBar != null) {
+            "Activity $activity does not have an ActionBar set via setSupportActionBar()"
+        }
+        actionBar.setDisplayHomeAsUpEnabled(true)
+        val delegate = activity.drawerToggleDelegate
+        require(delegate != null) {
+            "Activity $activity does not have an DrawerToggleDelegate set"
+        }
+        delegate.setActionBarUpIndicator(icon, contentDescription)
+    }
+}
diff --git a/wear/wear-tiles/build.gradle b/wear/wear-tiles/build.gradle
index fc0e747..bcfeda5 100644
--- a/wear/wear-tiles/build.gradle
+++ b/wear/wear-tiles/build.gradle
@@ -38,11 +38,20 @@
     api(GUAVA_LISTENABLE_FUTURE)
     implementation(PROTOBUF_LITE)
     implementation 'androidx.annotation:annotation:1.2.0-alpha01'
+
+    testImplementation(ANDROIDX_TEST_EXT_JUNIT)
+    testImplementation(ANDROIDX_TEST_EXT_TRUTH)
+    testImplementation(ANDROIDX_TEST_CORE)
+    testImplementation(ANDROIDX_TEST_RUNNER)
+    testImplementation(ANDROIDX_TEST_RULES)
+    testImplementation(ROBOLECTRIC)
+    testImplementation(MOCKITO_CORE)
 }
 
 android {
     defaultConfig {
-        minSdkVersion 24
+        minSdkVersion 26
+        targetSdkVersion 28
     }
     buildFeatures {
         aidl = true
diff --git a/wear/wear-tiles/src/main/AndroidManifest.xml b/wear/wear-tiles/src/main/AndroidManifest.xml
index e26eded..262aeeb 100644
--- a/wear/wear-tiles/src/main/AndroidManifest.xml
+++ b/wear/wear-tiles/src/main/AndroidManifest.xml
@@ -1,4 +1,2 @@
 <?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="androidx.wear.tiles">
-</manifest>
+<manifest package="androidx.wear.tiles" />
\ No newline at end of file
diff --git a/wear/wear-tiles/src/main/java/androidx/wear/tiles/ProtoParcelable.java b/wear/wear-tiles/src/main/java/androidx/wear/tiles/ProtoParcelable.java
index 06b437c..a99c172 100644
--- a/wear/wear-tiles/src/main/java/androidx/wear/tiles/ProtoParcelable.java
+++ b/wear/wear-tiles/src/main/java/androidx/wear/tiles/ProtoParcelable.java
@@ -106,6 +106,6 @@
 
     @Override
     public int hashCode() {
-        return Arrays.hashCode(mContents);
+        return 31 * mVersion + Arrays.hashCode(mContents);
     }
 }
diff --git a/wear/wear-tiles/src/test/java/androidx/wear/tiles/ProtoParcelableTest.java b/wear/wear-tiles/src/test/java/androidx/wear/tiles/ProtoParcelableTest.java
new file mode 100644
index 0000000..d5f1cea
--- /dev/null
+++ b/wear/wear-tiles/src/test/java/androidx/wear/tiles/ProtoParcelableTest.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.tiles;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Parcel;
+
+import androidx.wear.tiles.proto.RequestProto;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
+
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument // See http://g/robolectric-users/fTi2FRXgyGA/m/PkB0wYuwBgAJ
+public final class ProtoParcelableTest {
+    public static class Wrapper extends ProtoParcelable {
+        public static final int VERSION = 1;
+        public static final Creator<Wrapper> CREATOR = newCreator(Wrapper.class, Wrapper::new);
+
+        Wrapper(byte[] payload, int version) {
+            super(payload, version);
+        }
+    }
+
+    @Test
+    public void contentsEqualsAndHashCode() {
+        final Wrapper foo1 =
+                new Wrapper(RequestProto.ResourcesRequest.newBuilder().setVersion(
+                        "foo").build().toByteArray(), Wrapper.VERSION);
+        final Wrapper foo2 =
+                new Wrapper(RequestProto.ResourcesRequest.newBuilder().setVersion(
+                        "foo").build().toByteArray(), Wrapper.VERSION);
+        final Wrapper bar =
+                new Wrapper(RequestProto.ResourcesRequest.newBuilder().setVersion(
+                        "bar").build().toByteArray(), Wrapper.VERSION);
+        assertThat(foo1).isEqualTo(foo2);
+        assertThat(foo1).isNotEqualTo(bar);
+        assertThat(foo1.hashCode()).isEqualTo(foo2.hashCode());
+        assertThat(foo1.hashCode()).isNotEqualTo(bar.hashCode());
+    }
+
+    @Test
+    public void versionEqualsAndHashCode() {
+        final Wrapper foo1 =
+                new Wrapper(RequestProto.ResourcesRequest.newBuilder().setVersion(
+                        "foo").build().toByteArray(), Wrapper.VERSION);
+        final Wrapper foo2 =
+                new Wrapper(RequestProto.ResourcesRequest.newBuilder().setVersion(
+                        "foo").build().toByteArray(), /* version= */2);
+
+        assertThat(foo1).isNotEqualTo(foo2);
+        assertThat(foo1.hashCode()).isNotEqualTo(foo2.hashCode());
+    }
+
+    @Test
+    public void toParcelAndBack() {
+        RequestProto.ResourcesRequest wrappedMessage =
+                RequestProto.ResourcesRequest.newBuilder().setVersion("foobar").build();
+        Wrapper wrapper = new Wrapper(wrappedMessage.toByteArray(), Wrapper.VERSION);
+
+        Parcel parcel = Parcel.obtain();
+        wrapper.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+        assertThat(Wrapper.CREATOR.createFromParcel(parcel)).isEqualTo(wrapper);
+    }
+
+    @Test
+    public void arrayCreator() {
+        assertThat(Wrapper.CREATOR.newArray(123)).hasLength(123);
+    }
+}
diff --git a/wear/wear-tiles/src/test/java/androidx/wear/tiles/ResourcesDataTest.java b/wear/wear-tiles/src/test/java/androidx/wear/tiles/ResourcesDataTest.java
new file mode 100644
index 0000000..9465918
--- /dev/null
+++ b/wear/wear-tiles/src/test/java/androidx/wear/tiles/ResourcesDataTest.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.tiles;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Parcel;
+
+import androidx.wear.tiles.proto.ResourceProto.Resources;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
+
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
+public final class ResourcesDataTest {
+    @Test
+    public void toParcelAndBack() {
+        Resources resources = Resources.newBuilder().setVersion("v123").build();
+        ResourcesData wrapper =
+                new ResourcesData(resources.toByteArray(), ResourcesData.VERSION_PROTOBUF);
+
+        Parcel parcel = Parcel.obtain();
+        wrapper.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+        assertThat(ResourcesData.CREATOR.createFromParcel(parcel)).isEqualTo(wrapper);
+    }
+}
diff --git a/wear/wear-tiles/src/test/java/androidx/wear/tiles/ResourcesRequestDataTest.java b/wear/wear-tiles/src/test/java/androidx/wear/tiles/ResourcesRequestDataTest.java
new file mode 100644
index 0000000..9213528
--- /dev/null
+++ b/wear/wear-tiles/src/test/java/androidx/wear/tiles/ResourcesRequestDataTest.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.tiles;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Parcel;
+
+import androidx.wear.tiles.proto.RequestProto;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
+
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
+public final class ResourcesRequestDataTest {
+    @Test
+    public void toParcelAndBack() {
+        RequestProto.ResourcesRequest request =
+                RequestProto.ResourcesRequest.newBuilder().setVersion("v123").build();
+        ResourcesRequestData wrapper =
+                new ResourcesRequestData(request.toByteArray(),
+                        ResourcesRequestData.VERSION_PROTOBUF);
+
+        Parcel parcel = Parcel.obtain();
+        wrapper.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+        assertThat(ResourcesRequestData.CREATOR.createFromParcel(parcel)).isEqualTo(wrapper);
+    }
+}
diff --git a/wear/wear-tiles/src/test/java/androidx/wear/tiles/TileDataTest.java b/wear/wear-tiles/src/test/java/androidx/wear/tiles/TileDataTest.java
new file mode 100644
index 0000000..f1938b6
--- /dev/null
+++ b/wear/wear-tiles/src/test/java/androidx/wear/tiles/TileDataTest.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.tiles;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Parcel;
+
+import androidx.wear.tiles.proto.TileProto.Tile;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
+
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
+public final class TileDataTest {
+    @Test
+    public void toParcelAndBack() {
+        Tile tile = Tile.newBuilder().setResourcesVersion("v123").build();
+        TileData wrapper = new TileData(tile.toByteArray(), TileData.VERSION_PROTOBUF);
+
+        Parcel parcel = Parcel.obtain();
+        wrapper.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+        assertThat(TileData.CREATOR.createFromParcel(parcel)).isEqualTo(wrapper);
+    }
+}
diff --git a/wear/wear-tiles/src/test/java/androidx/wear/tiles/TileProviderServiceTest.java b/wear/wear-tiles/src/test/java/androidx/wear/tiles/TileProviderServiceTest.java
new file mode 100644
index 0000000..7f5f73d
--- /dev/null
+++ b/wear/wear-tiles/src/test/java/androidx/wear/tiles/TileProviderServiceTest.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.tiles;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.robolectric.Shadows.shadowOf;
+
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.Looper;
+
+import androidx.annotation.NonNull;
+import androidx.wear.tiles.builders.ResourceBuilders;
+import androidx.wear.tiles.builders.TileBuilders;
+import androidx.wear.tiles.proto.RequestProto;
+import androidx.wear.tiles.proto.ResourceProto.Resources;
+import androidx.wear.tiles.proto.TileProto.Tile;
+import androidx.wear.tiles.readers.RequestReaders.ResourcesRequest;
+import androidx.wear.tiles.readers.RequestReaders.TileRequest;
+
+import com.google.common.truth.Expect;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.robolectric.Robolectric;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.android.controller.ServiceController;
+import org.robolectric.annotation.internal.DoNotInstrument;
+
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
+public class TileProviderServiceTest {
+    @Rule public final Expect expect = Expect.create();
+
+    private static final TileBuilders.Tile DUMMY_TILE =
+            TileBuilders.Tile.builder().setResourcesVersion("5").build();
+    private TileProvider mTileProviderService;
+
+    @Mock private TileCallback mMockTileCallback;
+    @Mock private ResourcesCallback mMockResourcesCallback;
+
+    @Before
+    public void setUp() {
+        mMockTileCallback = mock(TileCallback.class);
+        mMockResourcesCallback = mock(ResourcesCallback.class);
+
+        ServiceController<DummyTileProviderService> dummyTileProviderServiceController =
+                Robolectric.buildService(
+                        DummyTileProviderService.class);
+
+        Intent i = new Intent(TileProviderService.ACTION_BIND_TILE_PROVIDER);
+        IBinder binder = dummyTileProviderServiceController.get().onBind(i);
+        mTileProviderService = TileProvider.Stub.asInterface(binder);
+    }
+
+    @Test
+    public void tileProvider_tileRequest() throws Exception {
+        mTileProviderService.onTileRequest(
+                5,
+                new TileRequestData(
+                        RequestProto.TileRequest.getDefaultInstance().toByteArray(),
+                        TileRequestData.VERSION_PROTOBUF),
+                mMockTileCallback);
+
+        shadowOf(Looper.getMainLooper()).idle();
+
+        ArgumentCaptor<TileData> tileCaptor = ArgumentCaptor.forClass(TileData.class);
+
+        verify(mMockTileCallback).updateTileData(tileCaptor.capture());
+
+        Tile tile =
+                Tile.parseFrom(
+                        tileCaptor.getValue().getContents());
+
+        expect.that(tile).isEqualTo(DUMMY_TILE.toProto());
+    }
+
+    @Test
+    public void tileProvider_resourcesRequest() throws Exception {
+        final String resourcesVersion = "HELLO WORLD";
+
+        ResourcesRequestData resourcesRequestData =
+                new ResourcesRequestData(
+                        RequestProto.ResourcesRequest.newBuilder()
+                                .setVersion(resourcesVersion)
+                                .build()
+                                .toByteArray(),
+                        ResourcesRequestData.VERSION_PROTOBUF);
+
+        mTileProviderService.onResourcesRequest(5, resourcesRequestData, mMockResourcesCallback);
+
+        shadowOf(Looper.getMainLooper()).idle();
+
+        ArgumentCaptor<ResourcesData> resourcesCaptor = ArgumentCaptor.forClass(
+                ResourcesData.class);
+        verify(mMockResourcesCallback).updateResources(resourcesCaptor.capture());
+
+        Resources resources =
+                Resources.parseFrom(
+                        resourcesCaptor.getValue().getContents());
+
+        expect.that(resources.getVersion()).isEqualTo(resourcesVersion);
+    }
+
+    public static class DummyTileProviderService extends TileProviderService {
+        @Override
+        @NonNull
+        protected ListenableFuture<TileBuilders.Tile> onTileRequest(
+                @NonNull TileRequest requestParams) {
+            return Futures.immediateFuture(DUMMY_TILE);
+        }
+
+        @Override
+        @NonNull
+        protected ListenableFuture<ResourceBuilders.Resources> onResourcesRequest(
+                @NonNull ResourcesRequest requestParams) {
+            ResourceBuilders.Resources resources =
+                    ResourceBuilders.Resources.builder().setVersion(
+                            requestParams.getVersion()).build();
+
+            return Futures.immediateFuture(resources);
+        }
+    }
+}
diff --git a/wear/wear-tiles/src/test/java/androidx/wear/tiles/TileRequestDataTest.java b/wear/wear-tiles/src/test/java/androidx/wear/tiles/TileRequestDataTest.java
new file mode 100644
index 0000000..bf8ad22
--- /dev/null
+++ b/wear/wear-tiles/src/test/java/androidx/wear/tiles/TileRequestDataTest.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.tiles;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Parcel;
+
+import androidx.wear.tiles.proto.RequestProto;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
+
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
+public final class TileRequestDataTest {
+    @Test
+    public void toParcelAndBack() {
+        RequestProto.TileRequest request = RequestProto.TileRequest.getDefaultInstance();
+        TileRequestData wrapper =
+                new TileRequestData(request.toByteArray(), TileRequestData.VERSION_PROTOBUF);
+
+        Parcel parcel = Parcel.obtain();
+        wrapper.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+        assertThat(TileRequestData.CREATOR.createFromParcel(parcel)).isEqualTo(wrapper);
+    }
+}
diff --git a/wear/wear-tiles/src/test/java/androidx/wear/tiles/TileUpdateRequestDataTest.java b/wear/wear-tiles/src/test/java/androidx/wear/tiles/TileUpdateRequestDataTest.java
new file mode 100644
index 0000000..73f336c
--- /dev/null
+++ b/wear/wear-tiles/src/test/java/androidx/wear/tiles/TileUpdateRequestDataTest.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.tiles;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Parcel;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
+
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
+public final class TileUpdateRequestDataTest {
+    @Test
+    public void toParcelAndBack() {
+        // This payload ends up empty anyway (it's there for future expansion). Just test that it
+        // doesn't error out when being parceled and back again.
+        TileUpdateRequestData wrapper = new TileUpdateRequestData();
+
+        Parcel parcel = Parcel.obtain();
+        wrapper.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+        assertThat(TileUpdateRequestData.CREATOR.createFromParcel(parcel)).isEqualTo(wrapper);
+    }
+}