Merge "Fix bug in getAllMethods for pkg-private methods" into androidx-main
diff --git a/buildSrc/repos.gradle b/buildSrc/repos.gradle
index bfc3ec4..8b0890b 100644
--- a/buildSrc/repos.gradle
+++ b/buildSrc/repos.gradle
@@ -30,6 +30,14 @@
  * Adds maven repositories to the given repository handler.
  */
 def addMavenRepositories(RepositoryHandler handler) {
+    def metalavaRepoOverride = System.getenv("METALAVA_REPO")
+    if (metalavaRepoOverride != null) {
+        for(extraRepo in metalavaRepoOverride.split(File.pathSeparator)) {
+            handler.maven {
+                url extraRepo
+            }
+        }
+    }
     handler.maven {
         url("${repos.prebuiltsRoot}/androidx/internal")
         metadataSources {
@@ -72,6 +80,7 @@
         }
         handler.mavenLocal()
     }
+    // Ordering appears to be important: b/229733266
     def androidPluginRepoOverride = System.getenv("GRADLE_PLUGIN_REPO")
     if (androidPluginRepoOverride != null) {
         for(extraRepo in androidPluginRepoOverride.split(File.pathSeparator)) {
diff --git a/busytown/androidx_with_metalava.sh b/busytown/androidx_with_metalava.sh
old mode 100644
new mode 100755
index dd307b5..b8afb42
--- a/busytown/androidx_with_metalava.sh
+++ b/busytown/androidx_with_metalava.sh
@@ -1,2 +1,9 @@
-#!/bin/bash
-EXIT_STATUS=0
+set -e
+SCRIPT_PATH="$(cd $(dirname $0) && pwd)"
+
+$SCRIPT_PATH/impl/build-metalava-and-androidx.sh \
+  -Pandroidx.allWarningsAsErrors \
+  -Pandroidx.verifyUpToDate \
+  listTaskOutputs \
+  checkApi \
+  --stacktrace
diff --git a/busytown/impl/build-metalava-and-androidx.sh b/busytown/impl/build-metalava-and-androidx.sh
new file mode 100755
index 0000000..89d6fa4
--- /dev/null
+++ b/busytown/impl/build-metalava-and-androidx.sh
@@ -0,0 +1,67 @@
+#!/bin/bash
+set -e
+
+echo "Starting $0 at $(date)"
+
+androidxArguments="$*"
+
+WORKING_DIR="$(pwd)"
+SCRIPTS_DIR="$(cd $(dirname $0)/.. && pwd)"
+cd "$SCRIPTS_DIR/../../.."
+echo "Script running from $(pwd)"
+
+# resolve dirs
+export OUT_DIR=$(pwd)/out
+
+if [ -z "$DIST_DIR" ]; then
+  DIST_DIR="$OUT_DIR/dist"
+fi
+mkdir -p "$DIST_DIR"
+
+export DIST_DIR="$DIST_DIR"
+
+# resolve GRADLE_USER_HOME
+export GRADLE_USER_HOME="$DIST_DIR/gradle"
+mkdir -p "$GRADLE_USER_HOME"
+
+if [ "$ROOT_DIR" == "" ]; then
+  ROOT_DIR="$WORKING_DIR"
+else
+  ROOT_DIR="$(cd $ROOT_DIR && pwd)"
+fi
+
+METALAVA_DIR=$ROOT_DIR/tools/metalava
+gw="$METALAVA_DIR/gradlew -Dorg.gradle.jvmargs=-Xmx24g"
+
+# Use androidx prebuilt since we don't have metalava prebuilts
+export ANDROID_HOME="$WORKING_DIR/../../prebuilts/fullsdk-linux/platforms/android-31/android.jar"
+
+function buildMetalava() {
+  METALAVA_BUILD_LOG="$OUT_DIR/metalava.log"
+  if $gw -p $METALAVA_DIR createArchive --stacktrace --no-daemon > "$METALAVA_BUILD_LOG" 2>&1; then
+    echo built metalava successfully
+  else
+    cat "$METALAVA_BUILD_LOG" >&2
+    echo failed to build metalava
+    return 1
+  fi
+
+}
+
+buildMetalava
+
+# Mac grep doesn't support -P, so use perl version of `grep -oP "(?<=metalavaVersion=).*"`
+export METALAVA_VERSION=`perl -nle'print $& while m{(?<=metalavaVersion=).*}g' $METALAVA_DIR/src/main/resources/version.properties`
+export METALAVA_REPO="$ROOT_DIR/out/dist/repo/m2repository"
+export JAVA_TOOLS_JAR="$(pwd)/prebuilts/jdk/jdk8/$PREBUILT_JDK/lib/tools.jar"
+
+function buildAndroidx() {
+  LOG_PROCESSOR="$SCRIPTS_DIR/../development/build_log_processor.sh"
+  properties="-Pandroidx.summarizeStderr --no-daemon"
+  "$LOG_PROCESSOR" $gw $properties -p frameworks/support $androidxArguments \
+    --dependency-verification=off # building against tip of tree of metalava that potentially pulls in new dependencies
+
+}
+
+buildAndroidx
+echo "Completing $0 at $(date)"
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/Metadata.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/Metadata.kt
index 5692a9c..c65dda1 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/Metadata.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/Metadata.kt
@@ -178,7 +178,20 @@
  * default. These values are defined by camera2.
  */
 @JvmInline
-public value class RequestTemplate(public val value: Int)
+public value class RequestTemplate(public val value: Int) {
+    val name: String
+        get() {
+            return when (value) {
+                1 -> "TEMPLATE_PREVIEW"
+                2 -> "TEMPLATE_STILL_CAPTURE"
+                3 -> "TEMPLATE_RECORD"
+                4 -> "TEMPLATE_VIDEO_SNAPSHOT"
+                5 -> "TEMPLATE_ZERO_SHUTTER_LAG"
+                6 -> "TEMPLATE_MANUAL"
+                else -> "UNKNOWN-$value"
+            }
+        }
+}
 
 /**
  * A [RequestNumber] is an artificial identifier that is created for each request that is submitted
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/core/Debug.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/core/Debug.kt
index 8e8be61..61d12bd 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/core/Debug.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/core/Debug.kt
@@ -24,6 +24,7 @@
 import android.hardware.camera2.CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES
 import android.hardware.camera2.CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA
 import android.hardware.camera2.CaptureRequest
+import android.hardware.camera2.CaptureResult
 import android.os.Build
 import android.os.Trace
 import androidx.annotation.RequiresApi
@@ -72,6 +73,27 @@
         }
     }
 
+    private fun appendParameters(builder: StringBuilder, name: String, parameters: Map<*, Any?>) {
+        builder.apply {
+            if (parameters.isEmpty()) {
+                append("$name: (None)\n")
+            } else {
+                append("${name}\n")
+                val parametersString: List<Pair<String, Any?>> = parameters.map {
+                    when (val key = it.key) {
+                        is CameraCharacteristics.Key<*> -> key.name
+                        is CaptureRequest.Key<*> -> key.name
+                        is CaptureResult.Key<*> -> key.name
+                        else -> key.toString()
+                    } to it.value
+                }
+                parametersString.sortedBy { it.first }.forEach {
+                    append("  ${it.first.padEnd(50, ' ')}${it.second}\n")
+                }
+            }
+        }
+    }
+
     public fun formatCameraGraphProperties(
         metadata: CameraMetadata,
         graphConfig: CameraGraph.Config,
@@ -120,19 +142,13 @@
                 }
             }
 
-            if (graphConfig.sessionParameters.isEmpty()) {
-                append("Session Parameters: (None)")
-            } else {
-                append("Session Parameters:\n")
-                val captureRequestParameters = graphConfig.sessionParameters.filter {
-                    it is CaptureRequest.Key<*>
-                }
-                for (parameter in captureRequestParameters) {
-                    append("  ")
-                    append((parameter.key).name.padEnd(50, ' '))
-                    append(parameter.value)
-                }
-            }
+            append("Session Template: ${graphConfig.sessionTemplate.name}\n")
+            appendParameters(this, "Session Parameters", graphConfig.sessionParameters)
+
+            append("Default Template: ${graphConfig.defaultTemplate.name}\n")
+            appendParameters(this, "Default Parameters", graphConfig.defaultParameters)
+
+            appendParameters(this, "Required Parameters", graphConfig.requiredParameters)
         }.toString()
     }
 }
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraControlImpl.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraControlImpl.java
index 4754fde..6aaf0da 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraControlImpl.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraControlImpl.java
@@ -126,7 +126,8 @@
     private final ZoomControl mZoomControl;
     private final TorchControl mTorchControl;
     private final ExposureControl mExposureControl;
-    private final ZslControl mZslControl;
+    @VisibleForTesting
+    ZslControl mZslControl;
     private final Camera2CameraControl mCamera2CameraControl;
     private final Camera2CapturePipeline mCamera2CapturePipeline;
     @GuardedBy("mLock")
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraImpl.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraImpl.java
index 228f221..61c0b2e 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraImpl.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraImpl.java
@@ -1280,6 +1280,11 @@
             // Recreates the Builder to add extra config needed
             CaptureConfig.Builder builder = CaptureConfig.Builder.from(captureConfig);
 
+            if (captureConfig.getTemplateType() == CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG
+                    && captureConfig.getCameraCaptureResult() != null) {
+                builder.setCameraCaptureResult(captureConfig.getCameraCaptureResult());
+            }
+
             if (captureConfig.getSurfaces().isEmpty() && captureConfig.isUseRepeatingSurface()) {
                 // Checks and attaches repeating surface to the request if there's no surface
                 // has been already attached. If there's no valid repeating surface to be
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CapturePipeline.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CapturePipeline.java
index 43a658a..57cebb0 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CapturePipeline.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CapturePipeline.java
@@ -47,12 +47,14 @@
 import androidx.camera.camera2.internal.compat.workaround.UseTorchAsFlash;
 import androidx.camera.camera2.interop.ExperimentalCamera2Interop;
 import androidx.camera.core.ImageCaptureException;
+import androidx.camera.core.ImageProxy;
 import androidx.camera.core.Logger;
 import androidx.camera.core.impl.CameraCaptureCallback;
 import androidx.camera.core.impl.CameraCaptureFailure;
 import androidx.camera.core.impl.CameraCaptureMetaData.AeState;
 import androidx.camera.core.impl.CameraCaptureMetaData.AwbState;
 import androidx.camera.core.impl.CameraCaptureResult;
+import androidx.camera.core.impl.CameraCaptureResults;
 import androidx.camera.core.impl.CaptureConfig;
 import androidx.camera.core.impl.Quirks;
 import androidx.camera.core.impl.annotation.ExecutedBy;
@@ -269,7 +271,30 @@
             List<CaptureConfig> configsToSubmit = new ArrayList<>();
             for (CaptureConfig captureConfig : captureConfigs) {
                 CaptureConfig.Builder configBuilder = CaptureConfig.Builder.from(captureConfig);
-                applyStillCaptureTemplate(configBuilder, captureConfig);
+
+                // Dequeue image from buffer and enqueue into image writer for reprocessing. If
+                // succeeded, retrieve capture result and set into capture config.
+                CameraCaptureResult cameraCaptureResult = null;
+                if (captureConfig.getTemplateType() == CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG) {
+                    ImageProxy imageProxy =
+                            mCameraControl.getZslControl().dequeueImageFromBuffer();
+                    boolean isSuccess = imageProxy != null
+                            && mCameraControl.getZslControl().enqueueImageToImageWriter(
+                                        imageProxy);
+                    if (isSuccess) {
+                        cameraCaptureResult =
+                                CameraCaptureResults.retrieveCameraCaptureResult(
+                                        imageProxy.getImageInfo());
+                    }
+                }
+
+                if (cameraCaptureResult != null) {
+                    configBuilder.setCameraCaptureResult(cameraCaptureResult);
+                } else {
+                    // Apply still capture template type for regular still capture case
+                    applyStillCaptureTemplate(configBuilder, captureConfig);
+                }
+
                 if (mOverrideAeModeForStillCapture.shouldSetAeModeAlwaysFlash(flashMode)) {
                     applyAeModeQuirk(configBuilder);
                 }
@@ -314,7 +339,8 @@
                 // repeating template is TEMPLATE_RECORD. Note:
                 // TEMPLATE_VIDEO_SNAPSHOT is not supported on legacy device.
                 templateToModify = CameraDevice.TEMPLATE_VIDEO_SNAPSHOT;
-            } else if (captureConfig.getTemplateType() == CaptureConfig.TEMPLATE_TYPE_NONE) {
+            } else if (captureConfig.getTemplateType() == CaptureConfig.TEMPLATE_TYPE_NONE
+                    || captureConfig.getTemplateType() == CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG) {
                 templateToModify = CameraDevice.TEMPLATE_STILL_CAPTURE;
             }
 
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CaptureRequestBuilder.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CaptureRequestBuilder.java
index 70e4cf9..203c937 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CaptureRequestBuilder.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CaptureRequestBuilder.java
@@ -19,8 +19,11 @@
 import android.hardware.camera2.CameraAccessException;
 import android.hardware.camera2.CameraDevice;
 import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.TotalCaptureResult;
+import android.os.Build;
 import android.view.Surface;
 
+import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.OptIn;
@@ -28,6 +31,7 @@
 import androidx.camera.camera2.interop.CaptureRequestOptions;
 import androidx.camera.camera2.interop.ExperimentalCamera2Interop;
 import androidx.camera.core.Logger;
+import androidx.camera.core.impl.CameraCaptureResult;
 import androidx.camera.core.impl.CaptureConfig;
 import androidx.camera.core.impl.Config;
 import androidx.camera.core.impl.DeferrableSurface;
@@ -115,8 +119,17 @@
             return null;
         }
 
-        CaptureRequest.Builder builder = device.createCaptureRequest(
-                captureConfig.getTemplateType());
+        CaptureRequest.Builder builder;
+        CameraCaptureResult cameraCaptureResult = captureConfig.getCameraCaptureResult();
+        if (Build.VERSION.SDK_INT >= 23
+                && captureConfig.getTemplateType() == CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG
+                && cameraCaptureResult != null
+                && cameraCaptureResult.getCaptureResult() instanceof TotalCaptureResult) {
+            builder = Api23Impl.createReprocessCaptureRequest(
+                    device, (TotalCaptureResult) cameraCaptureResult.getCaptureResult());
+        } else {
+            builder = device.createCaptureRequest(captureConfig.getTemplateType());
+        }
 
         applyImplementationOptionToCaptureBuilder(builder,
                 captureConfig.getImplementationOptions());
@@ -165,4 +178,23 @@
 
         return builder.build();
     }
+
+    /**
+     * Nested class to avoid verification errors for methods introduced in Android 6.0 (API 23).
+     */
+    @RequiresApi(23)
+    static class Api23Impl {
+        private Api23Impl() {
+            // This class is not instantiable.
+        }
+
+        @DoNotInline
+        static CaptureRequest.Builder createReprocessCaptureRequest(
+                @NonNull CameraDevice cameraDevice,
+                @NonNull TotalCaptureResult totalCaptureResult)
+                throws CameraAccessException {
+            return cameraDevice.createReprocessCaptureRequest(totalCaptureResult);
+        }
+
+    }
 }
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/CaptureSession.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/CaptureSession.java
index 1381745..a5f55e9 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/CaptureSession.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/CaptureSession.java
@@ -651,6 +651,12 @@
                     CaptureConfig.Builder captureConfigBuilder = CaptureConfig.Builder.from(
                             captureConfig);
 
+                    if (captureConfig.getTemplateType() == CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG
+                            && captureConfig.getCameraCaptureResult() != null) {
+                        captureConfigBuilder.setCameraCaptureResult(
+                                captureConfig.getCameraCaptureResult());
+                    }
+
                     // The override priority for implementation options
                     // P1 Single capture options
                     // P2 CameraEventCallback onRepeating options
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ZslControl.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ZslControl.java
index be2a193..ec8c3c8 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ZslControl.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ZslControl.java
@@ -16,9 +16,12 @@
 
 package androidx.camera.camera2.internal;
 
+import android.media.ImageWriter;
 import android.util.Size;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.camera.core.ImageProxy;
 import androidx.camera.core.impl.SessionConfig;
 
 /**
@@ -45,4 +48,20 @@
      *                 be always ON.
      */
     void setZslDisabled(boolean disabled);
+
+    /**
+     * Dequeues {@link ImageProxy} from ring buffer.
+     *
+     * @return {@link ImageProxy}.
+     */
+    @Nullable
+    ImageProxy dequeueImageFromBuffer();
+
+    /**
+     * Enqueues image to {@link ImageWriter} for reprocessing capture request.
+     *
+     * @param imageProxy {@link ImageProxy} to enqueue.
+     * @return True if enqueuing image succeeded, otherwise false.
+     */
+    boolean enqueueImageToImageWriter(@NonNull ImageProxy imageProxy);
 }
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ZslControlImpl.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ZslControlImpl.java
index a57a454..405bb2d 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ZslControlImpl.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ZslControlImpl.java
@@ -26,14 +26,18 @@
 import android.hardware.camera2.CaptureResult;
 import android.hardware.camera2.TotalCaptureResult;
 import android.hardware.camera2.params.InputConfiguration;
+import android.media.Image;
 import android.media.ImageWriter;
+import android.os.Build;
 import android.util.Size;
 import android.view.Surface;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.annotation.OptIn;
 import androidx.annotation.RequiresApi;
 import androidx.camera.camera2.internal.compat.CameraCharacteristicsCompat;
+import androidx.camera.core.ExperimentalGetImage;
 import androidx.camera.core.ImageProxy;
 import androidx.camera.core.ImageReaderProxys;
 import androidx.camera.core.SafeCloseImageReaderProxy;
@@ -46,6 +50,7 @@
 import androidx.camera.core.internal.compat.ImageWriterCompat;
 
 import java.util.LinkedList;
+import java.util.NoSuchElementException;
 import java.util.Queue;
 
 /**
@@ -178,6 +183,30 @@
                 mReprocessingImageReader.getImageFormat()));
     }
 
+    @Nullable
+    @Override
+    public ImageProxy dequeueImageFromBuffer() {
+        ImageProxy imageProxy = null;
+        try {
+            imageProxy = mImageRingBuffer.remove();
+        } catch (NoSuchElementException e) {
+        }
+
+        return imageProxy;
+    }
+
+    @Override
+    public boolean enqueueImageToImageWriter(@NonNull ImageProxy imageProxy) {
+        @OptIn(markerClass = ExperimentalGetImage.class)
+        Image image = imageProxy.getImage();
+
+        if (Build.VERSION.SDK_INT >= 23 && mReprocessingImageWriter != null && image != null) {
+            ImageWriterCompat.queueInputImage(mReprocessingImageWriter, image);
+            return true;
+        }
+        return false;
+    }
+
     private void cleanup() {
         // We might need synchronization here when clearing ring buffer while image is enqueued
         // at the same time. Will test this case.
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ZslControlNoOpImpl.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ZslControlNoOpImpl.java
index 9087b0b..b6d8d8d 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ZslControlNoOpImpl.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ZslControlNoOpImpl.java
@@ -19,6 +19,8 @@
 import android.util.Size;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.camera.core.ImageProxy;
 import androidx.camera.core.impl.SessionConfig;
 
 /**
@@ -33,4 +35,15 @@
     @Override
     public void setZslDisabled(boolean disabled) {
     }
+
+    @Nullable
+    @Override
+    public ImageProxy dequeueImageFromBuffer() {
+        return null;
+    }
+
+    @Override
+    public boolean enqueueImageToImageWriter(@NonNull ImageProxy imageProxy) {
+        return false;
+    }
 }
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/Camera2CapturePipelineTest.kt b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/Camera2CapturePipelineTest.kt
index f831878..70e9f8d 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/Camera2CapturePipelineTest.kt
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/Camera2CapturePipelineTest.kt
@@ -26,6 +26,8 @@
 import android.hardware.camera2.CaptureRequest
 import android.hardware.camera2.CaptureResult
 import android.hardware.camera2.TotalCaptureResult
+import android.media.Image
+import android.media.ImageWriter
 import android.os.Build
 import android.view.Surface
 import androidx.camera.camera2.impl.Camera2ImplConfig
@@ -48,6 +50,9 @@
 import androidx.camera.core.impl.Quirks
 import androidx.camera.core.impl.SessionConfig
 import androidx.camera.core.impl.utils.futures.Futures
+import androidx.camera.core.internal.CameraCaptureResultImageInfo
+import androidx.camera.testing.fakes.FakeCameraCaptureResult
+import androidx.camera.testing.fakes.FakeImageProxy
 import androidx.concurrent.futures.await
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.filters.FlakyTest
@@ -137,11 +142,11 @@
 
             override fun preCapture(captureResult: TotalCaptureResult?): ListenableFuture<Boolean> {
                 preCaptureCountDown.countDown()
-                return Futures.immediateFuture(true)
+                return Futures.immediateFuture(false)
             }
 
             override fun isCaptureResultNeeded(): Boolean {
-                return true
+                return false
             }
 
             override fun postCapture() {
@@ -544,6 +549,97 @@
     }
 
     @Test
+    fun submitZslCaptureRequests_withZslTemplate_templateZeroShutterLagSent(): Unit = runBlocking {
+        // Arrange.
+        val imageCaptureConfig = CaptureConfig.Builder().let {
+            it.addSurface(fakeStillCaptureSurface)
+            it.templateType = CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG
+            it.build()
+        }
+
+        val cameraControl = createCameraControl().apply {
+            simulateRepeatingResult(initialDelay = 100)
+        }
+
+        val zslControl = ZslControlImpl(createCameraCharacteristicsCompat(
+            hasCapabilities = true,
+            isYuvReprocessingSupported = true,
+            isPrivateReprocessingSupported = true
+        ))
+
+        val captureResult = FakeCameraCaptureResult()
+        val imageProxy = FakeImageProxy(CameraCaptureResultImageInfo(captureResult))
+        imageProxy.image = mock(Image::class.java)
+        zslControl.mImageRingBuffer.add(imageProxy)
+        zslControl.mReprocessingImageWriter = mock(ImageWriter::class.java)
+
+        cameraControl.mZslControl = zslControl
+
+        // Act.
+        cameraControl.submitStillCaptureRequests(
+            listOf(imageCaptureConfig),
+            ImageCapture.CAPTURE_MODE_ZERO_SHUTTER_LAG,
+            FLASH_MODE_OFF,
+        ).await()
+
+        // Assert.
+        val templateTypeToVerify = if (Build.VERSION.SDK_INT >= 23)
+            CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG
+        else CameraDevice.TEMPLATE_STILL_CAPTURE
+
+        immediateCompleteCapture.verifyRequestResult { captureConfigList ->
+            captureConfigList.filter {
+                it.surfaces.contains(fakeStillCaptureSurface)
+            }.map { captureConfig ->
+                captureConfig.templateType
+            }.contains(templateTypeToVerify)
+        }
+    }
+
+    @Test
+    fun submitZslCaptureRequests_withNoTemplate_templateStillPictureSent(): Unit = runBlocking {
+        // Arrange.
+        val imageCaptureConfig = CaptureConfig.Builder().let {
+            it.addSurface(fakeStillCaptureSurface)
+            it.build()
+        }
+
+        val cameraControl = createCameraControl().apply {
+            simulateRepeatingResult(initialDelay = 100)
+        }
+
+        val zslControl = ZslControlImpl(createCameraCharacteristicsCompat(
+            hasCapabilities = true,
+            isYuvReprocessingSupported = true,
+            isPrivateReprocessingSupported = true
+        ))
+
+        val captureResult = FakeCameraCaptureResult()
+        val imageProxy = FakeImageProxy(CameraCaptureResultImageInfo(captureResult))
+        imageProxy.image = mock(Image::class.java)
+        zslControl.mImageRingBuffer.add(imageProxy)
+        zslControl.mReprocessingImageWriter = mock(ImageWriter::class.java)
+
+        cameraControl.mZslControl = zslControl
+
+        // Act.
+        cameraControl.submitStillCaptureRequests(
+            listOf(imageCaptureConfig),
+            ImageCapture.CAPTURE_MODE_ZERO_SHUTTER_LAG,
+            FLASH_MODE_OFF,
+        ).await()
+
+        // Assert.
+        immediateCompleteCapture.verifyRequestResult { captureConfigList ->
+            captureConfigList.filter {
+                it.surfaces.contains(fakeStillCaptureSurface)
+            }.map { captureConfig ->
+                captureConfig.templateType
+            }.contains(CameraDevice.TEMPLATE_STILL_CAPTURE)
+        }
+    }
+
+    @Test
     fun captureFailure_taskShouldFailure() {
         // Arrange.
         val immediateFailureCapture =
@@ -854,16 +950,14 @@
         period: Long = 100, // in milliseconds
         resultParameters: Map<CaptureResult.Key<*>, Any> = mutableMapOf(),
     ) {
-        executorService.schedule({
-            runningRepeatingStream = executorService.scheduleAtFixedRate({
-                val tagBundle = sessionConfig.repeatingCaptureConfig.tagBundle
-                val requestOptions = sessionConfig.repeatingCaptureConfig.implementationOptions
-                val resultOptions = baseRepeatingResult.toMutableMap().apply {
-                    putAll(resultParameters)
-                }
-                sendRepeatingResult(tagBundle, requestOptions.toParameters(), resultOptions)
-            }, 0, period, TimeUnit.MILLISECONDS)
-        }, initialDelay, TimeUnit.MILLISECONDS)
+        runningRepeatingStream = executorService.scheduleAtFixedRate({
+            val tagBundle = sessionConfig.repeatingCaptureConfig.tagBundle
+            val requestOptions = sessionConfig.repeatingCaptureConfig.implementationOptions
+            val resultOptions = baseRepeatingResult.toMutableMap().apply {
+                putAll(resultParameters)
+            }
+            sendRepeatingResult(tagBundle, requestOptions.toParameters(), resultOptions)
+        }, initialDelay, period, TimeUnit.MILLISECONDS)
     }
 
     private fun Camera2CameraControlImpl.sendRepeatingResult(
@@ -1032,4 +1126,32 @@
 
         return parameters
     }
+
+    private fun createCameraCharacteristicsCompat(
+        hasCapabilities: Boolean,
+        isYuvReprocessingSupported: Boolean,
+        isPrivateReprocessingSupported: Boolean
+    ): CameraCharacteristicsCompat {
+        val characteristics = ShadowCameraCharacteristics.newCameraCharacteristics()
+        val shadowCharacteristics = Shadow.extract<ShadowCameraCharacteristics>(characteristics)
+
+        val capabilities = arrayListOf<Int>()
+        if (isYuvReprocessingSupported) {
+            capabilities.add(
+                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING)
+        }
+        if (isPrivateReprocessingSupported) {
+            capabilities.add(
+                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING)
+        }
+
+        if (hasCapabilities) {
+            shadowCharacteristics.set(
+                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES,
+                capabilities.toIntArray()
+            )
+        }
+
+        return CameraCharacteristicsCompat.toCameraCharacteristicsCompat(characteristics)
+    }
 }
\ No newline at end of file
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/CaptureConfig.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/CaptureConfig.java
index dd5208c..ad30da1 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/CaptureConfig.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/CaptureConfig.java
@@ -19,7 +19,6 @@
 import android.hardware.camera2.CameraCaptureSession;
 import android.hardware.camera2.CameraDevice;
 import android.hardware.camera2.CaptureRequest;
-import android.hardware.camera2.TotalCaptureResult;
 import android.view.Surface;
 
 import androidx.annotation.NonNull;
@@ -87,10 +86,10 @@
     private final TagBundle mTagBundle;
 
     /**
-     * The total capture result for reprocessable capture request.
+     * The camera capture result for reprocessing capture request.
      */
     @Nullable
-    private final TotalCaptureResult mTotalCaptureResult;
+    private final CameraCaptureResult mCameraCaptureResult;
 
     /**
      * Private constructor for a CaptureConfig.
@@ -104,7 +103,7 @@
      *                               must match the
      *                               constants defined by {@link CameraDevice}.
      * @param cameraCaptureCallbacks All camera capture callbacks.
-     * @param totalCaptureResult     The {@link TotalCaptureResult} for reprocessable capture
+     * @param cameraCaptureResult     The {@link CameraCaptureResult} for reprocessing capture
      *                               request.
      */
     CaptureConfig(
@@ -114,14 +113,14 @@
             List<CameraCaptureCallback> cameraCaptureCallbacks,
             boolean useRepeatingSurface,
             @NonNull TagBundle tagBundle,
-            @Nullable TotalCaptureResult totalCaptureResult) {
+            @Nullable CameraCaptureResult cameraCaptureResult) {
         mSurfaces = surfaces;
         mImplementationOptions = implementationOptions;
         mTemplateType = templateType;
         mCameraCaptureCallbacks = Collections.unmodifiableList(cameraCaptureCallbacks);
         mUseRepeatingSurface = useRepeatingSurface;
         mTagBundle = tagBundle;
-        mTotalCaptureResult = totalCaptureResult;
+        mCameraCaptureResult = cameraCaptureResult;
     }
 
     /** Returns an instance of a capture configuration with minimal configurations. */
@@ -131,14 +130,13 @@
     }
 
     /**
-     * Returns an instance of {@link TotalCaptureResult} for reprocessable capture request.
+     * Returns an instance of {@link CameraCaptureResult} for reprocessing capture request.
      *
-     * @return {@link TotalCaptureResult}.
-     * @see CameraDevice#createReprocessCaptureRequest(TotalCaptureResult)
+     * @return {@link CameraCaptureResult}.
      */
     @Nullable
-    public TotalCaptureResult getTotalCaptureResult() {
-        return mTotalCaptureResult;
+    public CameraCaptureResult getCameraCaptureResult() {
+        return mCameraCaptureResult;
     }
 
     /** Get all the surfaces that the request will write data to. */
@@ -201,7 +199,7 @@
         private boolean mUseRepeatingSurface = false;
         private MutableTagBundle mMutableTagBundle = MutableTagBundle.create();
         @Nullable
-        private TotalCaptureResult mTotalCaptureResult;
+        private CameraCaptureResult mCameraCaptureResult;
 
         public Builder() {
         }
@@ -243,12 +241,12 @@
         }
 
         /**
-         * Set the {@link TotalCaptureResult} for reprocessable capture request.
+         * Set the {@link CameraCaptureResult} for reprocessable capture request.
          *
-         * @param totalCaptureResult {@link TotalCaptureResult}.
+         * @param cameraCaptureResult {@link CameraCaptureResult}.
          */
-        public void setTotalCaptureResult(@Nullable TotalCaptureResult totalCaptureResult) {
-            mTotalCaptureResult = totalCaptureResult;
+        public void setCameraCaptureResult(@NonNull CameraCaptureResult cameraCaptureResult) {
+            mCameraCaptureResult = cameraCaptureResult;
         }
 
         public int getTemplateType() {
@@ -394,7 +392,7 @@
                     mCameraCaptureCallbacks,
                     mUseRepeatingSurface,
                     TagBundle.from(mMutableTagBundle),
-                    mTotalCaptureResult);
+                    mCameraCaptureResult);
         }
     }
 }
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/navigation/MapTemplateWithListDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/navigation/MapTemplateWithListDemoScreen.java
index 5ded59d..8bb250c 100644
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/navigation/MapTemplateWithListDemoScreen.java
+++ b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/navigation/MapTemplateWithListDemoScreen.java
@@ -59,9 +59,10 @@
         listBuilder.addItem(
                 new Row.Builder()
                         .setOnClickListener(
-                                ParkedOnlyOnClickListener.create(() -> onClick("Parked action")))
-                        .setTitle("Parked Only Title")
-                        .addText("More Parked only text.")
+                                ParkedOnlyOnClickListener.create(() -> onClick(
+                                        getCarContext().getString(R.string.parked_toast_msg))))
+                        .setTitle(getCarContext().getString(R.string.parked_only_title))
+                        .addText(getCarContext().getString(R.string.parked_only_text))
                         .build());
         // Some hosts may allow more items in the list than others, so create more.
         if (getCarContext().getCarAppApiLevel() > CarAppApiLevels.LEVEL_1) {
@@ -72,7 +73,7 @@
 
             for (int i = 2; i <= listLimit; ++i) {
                 // For row text, set text variants that fit best in different screen sizes.
-                String secondTextStr = "Second line of text";
+                String secondTextStr = getCarContext().getString(R.string.second_line_text);
                 CarText secondText =
                         new CarText.Builder(
                                 "================= " + secondTextStr + " ================")
@@ -80,12 +81,14 @@
                                         + " ----------------------")
                                 .addVariant(secondTextStr)
                                 .build();
-                final String onClickText = "Clicked row: " + i;
+                final String onClickText = getCarContext().getString(R.string.clicked_row_prefix)
+                        + ": " + i;
                 listBuilder.addItem(
                         new Row.Builder()
                                 .setOnClickListener(() -> onClick(onClickText))
-                                .setTitle("Title " + i)
-                                .addText("First line of text")
+                                .setTitle(
+                                        getCarContext().getString(R.string.title_prefix) + " " + i)
+                                .addText(getCarContext().getString(R.string.first_line_text))
                                 .addText(secondText)
                                 .build());
             }
@@ -105,7 +108,11 @@
                         .setOnClickListener(() -> {
                             CarToast.makeText(
                                             getCarContext(),
-                                            mIsFavorite ? "Not a favorite!" : "Favorite!",
+                                            mIsFavorite
+                                                    ? getCarContext().getString(
+                                                    R.string.not_favorite_toast_msg)
+                                                    : getCarContext().getString(
+                                                            R.string.favorite_toast_msg),
                                             LENGTH_SHORT)
                                     .show();
                             mIsFavorite = !mIsFavorite;
@@ -121,7 +128,7 @@
                                                 R.drawable.ic_close_white_24dp))
                                         .build())
                         .build())
-                .setTitle("Map Template with Pane Demo")
+                .setTitle(getCarContext().getString(R.string.map_template_list_demo_title))
                 .build();
 
 
@@ -135,7 +142,8 @@
                                 .setOnClickListener(
                                         () -> CarToast.makeText(
                                                         getCarContext(),
-                                                        "Bug reported!",
+                                                        getCarContext().getString(
+                                                                R.string.bug_reported_toast_msg),
                                                         CarToast.LENGTH_SHORT)
                                                 .show())
                                 .setIcon(
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/MiscTemplateDemosScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/MiscTemplateDemosScreen.java
index ac93876..b717963 100644
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/MiscTemplateDemosScreen.java
+++ b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/MiscTemplateDemosScreen.java
@@ -26,6 +26,7 @@
 import androidx.car.app.model.ListTemplate;
 import androidx.car.app.model.Row;
 import androidx.car.app.model.Template;
+import androidx.car.app.sample.showcase.common.R;
 
 /** An assortment of demos for different templates. */
 public final class MiscTemplateDemosScreen extends Screen {
@@ -44,21 +45,21 @@
     public Template onGetTemplate() {
         ItemList.Builder listBuilder = new ItemList.Builder();
         Row[] screenArray = new Row[]{
-                createRow("Pane Template Demo",
+                createRow(getCarContext().getString(R.string.pane_template_demo_title),
                         new PaneTemplateDemoScreen(getCarContext())),
-                createRow("List Template Demo",
+                createRow(getCarContext().getString(R.string.list_template_demo_title),
                         new ListTemplateDemoScreen(getCarContext())),
-                createRow("Place List Template Demo",
+                createRow(getCarContext().getString(R.string.place_list_template_demo_title),
                         new PlaceListTemplateBrowseDemoScreen(getCarContext())),
-                createRow("Search Template Demo",
+                createRow(getCarContext().getString(R.string.search_template_demo_title),
                         new SearchTemplateDemoScreen(getCarContext())),
-                createRow("Message Template Demo",
+                createRow(getCarContext().getString(R.string.msg_template_demo_title),
                         new MessageTemplateDemoScreen(getCarContext())),
-                createRow("Grid Template Demo",
+                createRow(getCarContext().getString(R.string.grid_template_demo_title),
                         new GridTemplateDemoScreen(getCarContext())),
-                createRow("Sign In Template Demo",
+                createRow(getCarContext().getString(R.string.sign_in_template_demo_title),
                         new SignInTemplateDemoScreen(getCarContext())),
-                createRow("Long Message Template Demo",
+                createRow(getCarContext().getString(R.string.long_msg_template_demo_title),
                         new LongMessageTemplateDemoScreen(getCarContext()))
         };
         // If the screenArray size is under the limit, we will show all of them on the first page.
@@ -77,13 +78,13 @@
         }
         ListTemplate.Builder builder = new ListTemplate.Builder()
                 .setSingleList(listBuilder.build())
-                .setTitle("Misc Templates Demos")
+                .setTitle(getCarContext().getString(R.string.misc_templates_demos_title))
                 .setHeaderAction(BACK);
         // If the current page does not cover the last item, we will show a More button
         if ((mPage + 1) * mItemLimit < screenArray.length && mPage + 1 < MAX_PAGES) {
             builder.setActionStrip(new ActionStrip.Builder()
                     .addAction(new Action.Builder()
-                            .setTitle("More")
+                            .setTitle(getCarContext().getString(R.string.more_action_title))
                             .setOnClickListener(() -> getScreenManager().push(
                                     new MiscTemplateDemosScreen(getCarContext(), mPage + 1,
                                             mItemLimit)))
diff --git a/car/app/app-samples/showcase/common/src/main/res/values/strings.xml b/car/app/app-samples/showcase/common/src/main/res/values/strings.xml
index 85119f8..16fe541 100644
--- a/car/app/app-samples/showcase/common/src/main/res/values/strings.xml
+++ b/car/app/app-samples/showcase/common/src/main/res/values/strings.xml
@@ -284,6 +284,7 @@
   <string name="browse_places_title">Browse Places</string>
 
   <!-- SearchTemplateDemoScreen -->
+  <string name="search_template_demo_title">Search Template Demo</string>
   <string name="search_hint">Search here</string>
 
   <!-- SignInTemplateDemoScreen -->
@@ -307,6 +308,7 @@
   <string name="provider_sign_in_instruction">Use this button to complete your Google sign-in</string>
   <string name="sign_in_complete_text">You are signed in!</string>
   <string name="sign_in_complete_title">Sign in completed</string>
+  <string name="sign_in_template_demo_title">Sign In Template Demo</string>
 
   <!-- ContentProviderIconsDemoScreen -->
   <string name="images_unknown_host_error">Images cannot be displayed for an unknown host</string>
diff --git a/collection/collection/src/jvmMain/kotlin/androidx/collection/CollectionPlatformUtils.kt b/collection/collection/src/jvmMain/kotlin/androidx/collection/CollectionPlatformUtils.jvm.kt
similarity index 100%
rename from collection/collection/src/jvmMain/kotlin/androidx/collection/CollectionPlatformUtils.kt
rename to collection/collection/src/jvmMain/kotlin/androidx/collection/CollectionPlatformUtils.jvm.kt
diff --git a/collection/collection/src/nativeMain/kotlin/androidx/collection/CollectionPlatformUtils.kt b/collection/collection/src/nativeMain/kotlin/androidx/collection/CollectionPlatformUtils.native.kt
similarity index 100%
rename from collection/collection/src/nativeMain/kotlin/androidx/collection/CollectionPlatformUtils.kt
rename to collection/collection/src/nativeMain/kotlin/androidx/collection/CollectionPlatformUtils.native.kt
diff --git a/compose/material3/material3/api/current.txt b/compose/material3/material3/api/current.txt
index 735f25a..dd6207d 100644
--- a/compose/material3/material3/api/current.txt
+++ b/compose/material3/material3/api/current.txt
@@ -117,6 +117,9 @@
     method @androidx.compose.runtime.Composable public static void TriStateCheckbox(androidx.compose.ui.state.ToggleableState state, kotlin.jvm.functions.Function0<kotlin.Unit>? onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.material3.CheckboxColors colors);
   }
 
+  public final class ChipKt {
+  }
+
   @androidx.compose.runtime.Stable public final class ColorScheme {
     ctor public ColorScheme(long primary, long onPrimary, long primaryContainer, long onPrimaryContainer, long inversePrimary, long secondary, long onSecondary, long secondaryContainer, long onSecondaryContainer, long tertiary, long onTertiary, long tertiaryContainer, long onTertiaryContainer, long background, long onBackground, long surface, long onSurface, long surfaceVariant, long onSurfaceVariant, long surfaceTint, long inverseSurface, long inverseOnSurface, long error, long onError, long errorContainer, long onErrorContainer, long outline);
     method public androidx.compose.material3.ColorScheme copy(optional long primary, optional long onPrimary, optional long primaryContainer, optional long onPrimaryContainer, optional long inversePrimary, optional long secondary, optional long onSecondary, optional long secondaryContainer, optional long onSecondaryContainer, optional long tertiary, optional long onTertiary, optional long tertiaryContainer, optional long onTertiaryContainer, optional long background, optional long onBackground, optional long surface, optional long onSurface, optional long surfaceVariant, optional long onSurfaceVariant, optional long surfaceTint, optional long inverseSurface, optional long inverseOnSurface, optional long error, optional long onError, optional long errorContainer, optional long onErrorContainer, optional long outline);
@@ -462,17 +465,21 @@
   }
 
   @androidx.compose.runtime.Stable public interface SwitchColors {
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> borderColor(boolean enabled, boolean checked);
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> iconColor(boolean enabled, boolean checked);
     method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> thumbColor(boolean enabled, boolean checked);
     method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> trackColor(boolean enabled, boolean checked);
   }
 
   public final class SwitchDefaults {
-    method @androidx.compose.runtime.Composable public androidx.compose.material3.SwitchColors colors(optional long checkedThumbColor, optional long checkedTrackColor, optional long uncheckedThumbColor, optional long uncheckedTrackColor, optional long disabledCheckedThumbColor, optional long disabledCheckedTrackColor, optional long disabledUncheckedThumbColor, optional long disabledUncheckedTrackColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.SwitchColors colors(optional long checkedThumbColor, optional long checkedTrackColor, optional long checkedBorderColor, optional long checkedIconColor, optional long uncheckedThumbColor, optional long uncheckedTrackColor, optional long uncheckedBorderColor, optional long uncheckedIconColor, optional long disabledCheckedThumbColor, optional long disabledCheckedTrackColor, optional long disabledCheckedBorderColor, optional long disabledCheckedIconColor, optional long disabledUncheckedThumbColor, optional long disabledUncheckedTrackColor, optional long disabledUncheckedBorderColor, optional long disabledUncheckedIconColor);
+    method public float getIconSize();
+    property public final float IconSize;
     field public static final androidx.compose.material3.SwitchDefaults INSTANCE;
   }
 
   public final class SwitchKt {
-    method @androidx.compose.runtime.Composable public static void Switch(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit>? onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.material3.SwitchColors colors);
+    method @androidx.compose.runtime.Composable public static void Switch(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit>? onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? thumbContent, optional boolean enabled, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.material3.SwitchColors colors);
   }
 
   public final class TabKt {
diff --git a/compose/material3/material3/api/public_plus_experimental_current.txt b/compose/material3/material3/api/public_plus_experimental_current.txt
index 98e9c45..43e0f74 100644
--- a/compose/material3/material3/api/public_plus_experimental_current.txt
+++ b/compose/material3/material3/api/public_plus_experimental_current.txt
@@ -22,6 +22,19 @@
     method @androidx.compose.runtime.Composable public static void SmallTopAppBar(kotlin.jvm.functions.Function0<kotlin.Unit> title, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit> navigationIcon, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> actions, optional androidx.compose.material3.TopAppBarColors colors, optional androidx.compose.material3.TopAppBarScrollBehavior? scrollBehavior);
   }
 
+  @androidx.compose.material3.ExperimentalMaterial3Api public final class AssistChipDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ChipBorder assistChipBorder(optional long borderColor, optional long disabledBorderColor, optional float borderWidth);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ChipColors assistChipColors(optional long containerColor, optional long labelColor, optional long leadingIconContentColor, optional long trailingIconContentColor, optional long disabledContainerColor, optional long disabledLabelColor, optional long disabledLeadingIconContentColor, optional long disabledTrailingIconContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ChipElevation assistChipElevation(optional float defaultElevation, optional float pressedElevation, optional float focusedElevation, optional float hoveredElevation, optional float draggedElevation, optional float disabledElevation);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ChipColors elevatedAssistChipColors(optional long containerColor, optional long labelColor, optional long leadingIconContentColor, optional long trailingIconContentColor, optional long disabledContainerColor, optional long disabledLabelColor, optional long disabledLeadingIconContentColor, optional long disabledTrailingIconContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ChipElevation elevatedAssistChipElevation(optional float defaultElevation, optional float pressedElevation, optional float focusedElevation, optional float hoveredElevation, optional float draggedElevation, optional float disabledElevation);
+    method public float getHeight();
+    method public float getIconSize();
+    property public final float Height;
+    property public final float IconSize;
+    field public static final androidx.compose.material3.AssistChipDefaults INSTANCE;
+  }
+
   public final class BadgeKt {
     method @androidx.compose.runtime.Composable public static void Badge(optional androidx.compose.ui.Modifier modifier, optional long containerColor, optional long contentColor, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit>? content);
     method @androidx.compose.runtime.Composable public static void BadgedBox(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> badge, optional androidx.compose.ui.Modifier modifier, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
@@ -124,6 +137,32 @@
     method @androidx.compose.runtime.Composable public static void TriStateCheckbox(androidx.compose.ui.state.ToggleableState state, kotlin.jvm.functions.Function0<kotlin.Unit>? onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.material3.CheckboxColors colors);
   }
 
+  @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Stable public interface ChipBorder {
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.foundation.BorderStroke> borderStroke(boolean enabled);
+  }
+
+  @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Stable public interface ChipColors {
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> containerColor(boolean enabled);
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> labelColor(boolean enabled);
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> leadingIconContentColor(boolean enabled);
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> trailingIconContentColor(boolean enabled);
+  }
+
+  @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Stable public interface ChipElevation {
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.unit.Dp> shadowElevation(boolean enabled, androidx.compose.foundation.interaction.InteractionSource interactionSource);
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.unit.Dp> tonalElevation(boolean enabled, androidx.compose.foundation.interaction.InteractionSource interactionSource);
+  }
+
+  public final class ChipKt {
+    method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void AssistChip(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> label, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.material3.ChipElevation? elevation, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.ChipBorder? border, optional androidx.compose.material3.ChipColors colors);
+    method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void ElevatedAssistChip(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> label, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.material3.ChipElevation? elevation, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.ChipBorder? border, optional androidx.compose.material3.ChipColors colors);
+    method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void ElevatedFilterChip(boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> label, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? selectedIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.material3.SelectableChipElevation? elevation, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.SelectableChipBorder? border, optional androidx.compose.material3.SelectableChipColors colors);
+    method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void ElevatedSuggestionChip(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> label, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? icon, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.material3.ChipElevation? elevation, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.ChipBorder? border, optional androidx.compose.material3.ChipColors colors);
+    method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void FilterChip(boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> label, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? selectedIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.material3.SelectableChipElevation? elevation, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.SelectableChipBorder? border, optional androidx.compose.material3.SelectableChipColors colors);
+    method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void InputChip(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> label, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? avatar, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.material3.ChipElevation? elevation, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.ChipBorder? border, optional androidx.compose.material3.ChipColors colors);
+    method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void SuggestionChip(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> label, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? icon, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.material3.ChipElevation? elevation, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.ChipBorder? border, optional androidx.compose.material3.ChipColors colors);
+  }
+
   @androidx.compose.runtime.Stable public final class ColorScheme {
     ctor public ColorScheme(long primary, long onPrimary, long primaryContainer, long onPrimaryContainer, long inversePrimary, long secondary, long onSecondary, long secondaryContainer, long onSecondaryContainer, long tertiary, long onTertiary, long tertiaryContainer, long onTertiaryContainer, long background, long onBackground, long surface, long onSurface, long surfaceVariant, long onSurfaceVariant, long surfaceTint, long inverseSurface, long inverseOnSurface, long error, long onError, long errorContainer, long onErrorContainer, long outline);
     method public androidx.compose.material3.ColorScheme copy(optional long primary, optional long onPrimary, optional long primaryContainer, optional long onPrimaryContainer, optional long inversePrimary, optional long secondary, optional long onSecondary, optional long secondaryContainer, optional long onSecondaryContainer, optional long tertiary, optional long onTertiary, optional long tertiaryContainer, optional long onTertiaryContainer, optional long background, optional long onBackground, optional long surface, optional long onSurface, optional long surfaceVariant, optional long onSurfaceVariant, optional long surfaceTint, optional long inverseSurface, optional long inverseOnSurface, optional long error, optional long onError, optional long errorContainer, optional long onErrorContainer, optional long outline);
@@ -282,6 +321,19 @@
     property public final int End;
   }
 
+  @androidx.compose.material3.ExperimentalMaterial3Api public final class FilterChipDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.SelectableChipColors elevatedFilterChipColors(optional long containerColor, optional long labelColor, optional long iconColor, optional long disabledContainerColor, optional long disabledLabelColor, optional long disabledLeadingIconColor, optional long disabledTrailingIconColor, optional long selectedContainerColor, optional long selectedLabelColor, optional long selectedLeadingIconColor, optional long selectedTrailingIconColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.SelectableChipElevation elevatedFilterChipElevation(optional float defaultElevation, optional float pressedElevation, optional float focusedElevation, optional float hoveredElevation, optional float draggedElevation, optional float disabledElevation);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.SelectableChipBorder filterChipBorder(optional long borderColor, optional long selectedBorderColor, optional long disabledBorderColor, optional long disabledSelectedBorderColor, optional float borderWidth, optional float selectedBorderWidth);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.SelectableChipColors filterChipColors(optional long containerColor, optional long labelColor, optional long iconColor, optional long disabledContainerColor, optional long disabledLabelColor, optional long disabledLeadingIconColor, optional long disabledTrailingIconColor, optional long selectedContainerColor, optional long selectedLabelColor, optional long selectedLeadingIconColor, optional long selectedTrailingIconColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.SelectableChipElevation filterChipElevation(optional float defaultElevation, optional float pressedElevation, optional float focusedElevation, optional float hoveredElevation, optional float draggedElevation, optional float disabledElevation);
+    method public float getHeight();
+    method public float getIconSize();
+    property public final float Height;
+    property public final float IconSize;
+    field public static final androidx.compose.material3.FilterChipDefaults INSTANCE;
+  }
+
   public final class FloatingActionButtonDefaults {
     method @androidx.compose.runtime.Composable public androidx.compose.material3.FloatingActionButtonElevation elevation(optional float defaultElevation, optional float pressedElevation, optional float focusedElevation, optional float hoveredElevation);
     method public float getLargeIconSize();
@@ -344,6 +396,19 @@
     method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> contentColor(boolean enabled, boolean checked);
   }
 
+  @androidx.compose.material3.ExperimentalMaterial3Api public final class InputChipDefaults {
+    method public float getAvatarSize();
+    method public float getHeight();
+    method public float getIconSize();
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ChipBorder inputChipBorder(optional long borderColor, optional long disabledBorderColor, optional float borderWidth);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ChipColors inputChipColors(optional long containerColor, optional long labelColor, optional long leadingIconContentColor, optional long trailingIconContentColor, optional long disabledContainerColor, optional long disabledLabelColor, optional long disabledLeadingIconContentColor, optional long disabledTrailingIconContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ChipElevation inputChipElevation(optional float defaultElevation, optional float pressedElevation, optional float focusedElevation, optional float hoveredElevation, optional float draggedElevation, optional float disabledElevation);
+    property public final float AvatarSize;
+    property public final float Height;
+    property public final float IconSize;
+    field public static final androidx.compose.material3.InputChipDefaults INSTANCE;
+  }
+
   public final class MaterialTheme {
     method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.compose.material3.ColorScheme getColorScheme();
     method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.compose.material3.Shapes getShapes();
@@ -467,6 +532,22 @@
     method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void Scaffold(optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit> topBar, optional kotlin.jvm.functions.Function0<kotlin.Unit> bottomBar, optional kotlin.jvm.functions.Function0<kotlin.Unit> snackbarHost, optional kotlin.jvm.functions.Function0<kotlin.Unit> floatingActionButton, optional int floatingActionButtonPosition, optional long containerColor, optional long contentColor, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.PaddingValues,kotlin.Unit> content);
   }
 
+  @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Stable public interface SelectableChipBorder {
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.foundation.BorderStroke> borderStroke(boolean enabled, boolean selected);
+  }
+
+  @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Stable public interface SelectableChipColors {
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> containerColor(boolean enabled, boolean selected);
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> labelColor(boolean enabled, boolean selected);
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> leadingIconContentColor(boolean enabled, boolean selected);
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> trailingIconContentColor(boolean enabled, boolean selected);
+  }
+
+  @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Stable public interface SelectableChipElevation {
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.unit.Dp> shadowElevation(boolean enabled, boolean selected, androidx.compose.foundation.interaction.InteractionSource interactionSource);
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.unit.Dp> tonalElevation(boolean enabled, boolean selected, androidx.compose.foundation.interaction.InteractionSource interactionSource);
+  }
+
   @androidx.compose.runtime.Immutable public final class Shapes {
     ctor public Shapes(optional androidx.compose.foundation.shape.CornerBasedShape extraSmall, optional androidx.compose.foundation.shape.CornerBasedShape small, optional androidx.compose.foundation.shape.CornerBasedShape medium, optional androidx.compose.foundation.shape.CornerBasedShape large, optional androidx.compose.foundation.shape.CornerBasedShape extraLarge);
     method public androidx.compose.material3.Shapes copy(optional androidx.compose.foundation.shape.CornerBasedShape extraSmall, optional androidx.compose.foundation.shape.CornerBasedShape small, optional androidx.compose.foundation.shape.CornerBasedShape medium, optional androidx.compose.foundation.shape.CornerBasedShape large, optional androidx.compose.foundation.shape.CornerBasedShape extraLarge);
@@ -557,6 +638,19 @@
   public final class Strings_androidKt {
   }
 
+  @androidx.compose.material3.ExperimentalMaterial3Api public final class SuggestionChipDefaults {
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ChipColors elevatedSuggestionChipColors(optional long containerColor, optional long labelColor, optional long iconContentColor, optional long disabledContainerColor, optional long disabledLabelColor, optional long disabledIconContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ChipElevation elevatedSuggestionChipElevation(optional float defaultElevation, optional float pressedElevation, optional float focusedElevation, optional float hoveredElevation, optional float draggedElevation, optional float disabledElevation);
+    method public float getHeight();
+    method public float getIconSize();
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ChipBorder suggestionChipBorder(optional long borderColor, optional long disabledBorderColor, optional float borderWidth);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ChipColors suggestionChipColors(optional long containerColor, optional long labelColor, optional long iconContentColor, optional long disabledContainerColor, optional long disabledLabelColor, optional long disabledIconContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.ChipElevation suggestionChipElevation(optional float defaultElevation, optional float pressedElevation, optional float focusedElevation, optional float hoveredElevation, optional float draggedElevation, optional float disabledElevation);
+    property public final float Height;
+    property public final float IconSize;
+    field public static final androidx.compose.material3.SuggestionChipDefaults INSTANCE;
+  }
+
   public final class SurfaceKt {
     method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void Surface(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape shape, optional long color, optional long contentColor, optional float tonalElevation, optional float shadowElevation, optional androidx.compose.foundation.BorderStroke? border, kotlin.jvm.functions.Function0<kotlin.Unit> content);
     method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void Surface(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional long color, optional long contentColor, optional float tonalElevation, optional float shadowElevation, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> content);
@@ -570,17 +664,21 @@
   }
 
   @androidx.compose.runtime.Stable public interface SwitchColors {
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> borderColor(boolean enabled, boolean checked);
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> iconColor(boolean enabled, boolean checked);
     method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> thumbColor(boolean enabled, boolean checked);
     method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> trackColor(boolean enabled, boolean checked);
   }
 
   public final class SwitchDefaults {
-    method @androidx.compose.runtime.Composable public androidx.compose.material3.SwitchColors colors(optional long checkedThumbColor, optional long checkedTrackColor, optional long uncheckedThumbColor, optional long uncheckedTrackColor, optional long disabledCheckedThumbColor, optional long disabledCheckedTrackColor, optional long disabledUncheckedThumbColor, optional long disabledUncheckedTrackColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.SwitchColors colors(optional long checkedThumbColor, optional long checkedTrackColor, optional long checkedBorderColor, optional long checkedIconColor, optional long uncheckedThumbColor, optional long uncheckedTrackColor, optional long uncheckedBorderColor, optional long uncheckedIconColor, optional long disabledCheckedThumbColor, optional long disabledCheckedTrackColor, optional long disabledCheckedBorderColor, optional long disabledCheckedIconColor, optional long disabledUncheckedThumbColor, optional long disabledUncheckedTrackColor, optional long disabledUncheckedBorderColor, optional long disabledUncheckedIconColor);
+    method public float getIconSize();
+    property public final float IconSize;
     field public static final androidx.compose.material3.SwitchDefaults INSTANCE;
   }
 
   public final class SwitchKt {
-    method @androidx.compose.runtime.Composable public static void Switch(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit>? onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.material3.SwitchColors colors);
+    method @androidx.compose.runtime.Composable public static void Switch(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit>? onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? thumbContent, optional boolean enabled, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.material3.SwitchColors colors);
   }
 
   public final class TabKt {
diff --git a/compose/material3/material3/api/restricted_current.txt b/compose/material3/material3/api/restricted_current.txt
index 735f25a..dd6207d 100644
--- a/compose/material3/material3/api/restricted_current.txt
+++ b/compose/material3/material3/api/restricted_current.txt
@@ -117,6 +117,9 @@
     method @androidx.compose.runtime.Composable public static void TriStateCheckbox(androidx.compose.ui.state.ToggleableState state, kotlin.jvm.functions.Function0<kotlin.Unit>? onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.material3.CheckboxColors colors);
   }
 
+  public final class ChipKt {
+  }
+
   @androidx.compose.runtime.Stable public final class ColorScheme {
     ctor public ColorScheme(long primary, long onPrimary, long primaryContainer, long onPrimaryContainer, long inversePrimary, long secondary, long onSecondary, long secondaryContainer, long onSecondaryContainer, long tertiary, long onTertiary, long tertiaryContainer, long onTertiaryContainer, long background, long onBackground, long surface, long onSurface, long surfaceVariant, long onSurfaceVariant, long surfaceTint, long inverseSurface, long inverseOnSurface, long error, long onError, long errorContainer, long onErrorContainer, long outline);
     method public androidx.compose.material3.ColorScheme copy(optional long primary, optional long onPrimary, optional long primaryContainer, optional long onPrimaryContainer, optional long inversePrimary, optional long secondary, optional long onSecondary, optional long secondaryContainer, optional long onSecondaryContainer, optional long tertiary, optional long onTertiary, optional long tertiaryContainer, optional long onTertiaryContainer, optional long background, optional long onBackground, optional long surface, optional long onSurface, optional long surfaceVariant, optional long onSurfaceVariant, optional long surfaceTint, optional long inverseSurface, optional long inverseOnSurface, optional long error, optional long onError, optional long errorContainer, optional long onErrorContainer, optional long outline);
@@ -462,17 +465,21 @@
   }
 
   @androidx.compose.runtime.Stable public interface SwitchColors {
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> borderColor(boolean enabled, boolean checked);
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> iconColor(boolean enabled, boolean checked);
     method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> thumbColor(boolean enabled, boolean checked);
     method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> trackColor(boolean enabled, boolean checked);
   }
 
   public final class SwitchDefaults {
-    method @androidx.compose.runtime.Composable public androidx.compose.material3.SwitchColors colors(optional long checkedThumbColor, optional long checkedTrackColor, optional long uncheckedThumbColor, optional long uncheckedTrackColor, optional long disabledCheckedThumbColor, optional long disabledCheckedTrackColor, optional long disabledUncheckedThumbColor, optional long disabledUncheckedTrackColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.SwitchColors colors(optional long checkedThumbColor, optional long checkedTrackColor, optional long checkedBorderColor, optional long checkedIconColor, optional long uncheckedThumbColor, optional long uncheckedTrackColor, optional long uncheckedBorderColor, optional long uncheckedIconColor, optional long disabledCheckedThumbColor, optional long disabledCheckedTrackColor, optional long disabledCheckedBorderColor, optional long disabledCheckedIconColor, optional long disabledUncheckedThumbColor, optional long disabledUncheckedTrackColor, optional long disabledUncheckedBorderColor, optional long disabledUncheckedIconColor);
+    method public float getIconSize();
+    property public final float IconSize;
     field public static final androidx.compose.material3.SwitchDefaults INSTANCE;
   }
 
   public final class SwitchKt {
-    method @androidx.compose.runtime.Composable public static void Switch(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit>? onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.material3.SwitchColors colors);
+    method @androidx.compose.runtime.Composable public static void Switch(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit>? onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? thumbContent, optional boolean enabled, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.material3.SwitchColors colors);
   }
 
   public final class TabKt {
diff --git a/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/model/Components.kt b/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/model/Components.kt
index 0e9c5fa..603a9a5 100644
--- a/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/model/Components.kt
+++ b/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/model/Components.kt
@@ -93,6 +93,19 @@
     examples = CheckboxesExamples
 )
 
+private val Chips = Component(
+    id = nextId(),
+    name = "Chips",
+    description = "Chips allow users to enter information, make selections, filter content, or" +
+        " trigger actions.",
+    // No chip icon
+    tintIcon = true,
+    guidelinesUrl = "$ComponentGuidelinesUrl/chips",
+    docsUrl = "$DocsUrl#chips",
+    sourceUrl = "$Material3SourceUrl/Chip.kt",
+    examples = ChipsExamples
+)
+
 private val Dialogs = Component(
     id = nextId(),
     name = "Dialogs",
@@ -298,6 +311,7 @@
     Buttons,
     Card,
     Checkboxes,
+    Chips,
     Dialogs,
     ExtendedFloatingActionButton,
     FloatingActionButtons,
diff --git a/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/model/Examples.kt b/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/model/Examples.kt
index 5239032..689d106 100644
--- a/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/model/Examples.kt
+++ b/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/model/Examples.kt
@@ -25,6 +25,7 @@
 import androidx.compose.material3.samples.AlertDialogSample
 import androidx.compose.material3.samples.AlertDialogWithIconSample
 import androidx.compose.material3.samples.AnimatedExtendedFloatingActionButtonSample
+import androidx.compose.material3.samples.AssistChipSample
 import androidx.compose.material3.samples.BottomAppBarWithFAB
 import androidx.compose.material3.samples.ButtonSample
 import androidx.compose.material3.samples.ButtonWithIconSample
@@ -36,8 +37,11 @@
 import androidx.compose.material3.samples.ClickableOutlinedCardSample
 import androidx.compose.material3.samples.DismissibleNavigationDrawerSample
 import androidx.compose.material3.samples.EditableExposedDropdownMenuSample
+import androidx.compose.material3.samples.ElevatedAssistChipSample
 import androidx.compose.material3.samples.ElevatedButtonSample
 import androidx.compose.material3.samples.ElevatedCardSample
+import androidx.compose.material3.samples.ElevatedFilterChipSample
+import androidx.compose.material3.samples.ElevatedSuggestionChipSample
 import androidx.compose.material3.samples.EnterAlwaysSmallTopAppBar
 import androidx.compose.material3.samples.ExitUntilCollapsedLargeTopAppBar
 import androidx.compose.material3.samples.ExitUntilCollapsedMediumTopAppBar
@@ -50,12 +54,16 @@
 import androidx.compose.material3.samples.FilledIconButtonSample
 import androidx.compose.material3.samples.FilledIconToggleButtonSample
 import androidx.compose.material3.samples.FilledTonalButtonSample
+import androidx.compose.material3.samples.FilterChipSample
+import androidx.compose.material3.samples.FilterChipWithLeadingIconSample
 import androidx.compose.material3.samples.FilledTonalIconButtonSample
 import androidx.compose.material3.samples.FilledTonalIconToggleButtonSample
 import androidx.compose.material3.samples.FloatingActionButtonSample
 import androidx.compose.material3.samples.IconButtonSample
 import androidx.compose.material3.samples.IconTabs
 import androidx.compose.material3.samples.IconToggleButtonSample
+import androidx.compose.material3.samples.InputChipSample
+import androidx.compose.material3.samples.InputChipWithAvatarSample
 import androidx.compose.material3.samples.LargeFloatingActionButtonSample
 import androidx.compose.material3.samples.LeadingIconTabs
 import androidx.compose.material3.samples.LinearProgressIndicatorSample
@@ -90,6 +98,8 @@
 import androidx.compose.material3.samples.SliderSample
 import androidx.compose.material3.samples.SmallFloatingActionButtonSample
 import androidx.compose.material3.samples.StepsSliderSample
+import androidx.compose.material3.samples.SuggestionChipSample
+import androidx.compose.material3.samples.SwitchWithThumbIconSample
 import androidx.compose.material3.samples.SwitchSample
 import androidx.compose.material3.samples.TextAndIconTabs
 import androidx.compose.material3.samples.TextArea
@@ -215,6 +225,74 @@
     }
 )
 
+private const val ChipsExampleDescription = "Chips examples"
+private const val ChipsExampleSourceUrl = "$SampleSourceUrl/ChipSamples.kt"
+val ChipsExamples = listOf(
+    Example(
+        name = ::AssistChipSample.name,
+        description = ChipsExampleDescription,
+        sourceUrl = ChipsExampleSourceUrl
+    ) {
+        AssistChipSample()
+    },
+    Example(
+        name = ::ElevatedAssistChipSample.name,
+        description = ChipsExampleDescription,
+        sourceUrl = ChipsExampleSourceUrl
+    ) {
+        ElevatedAssistChipSample()
+    },
+    Example(
+        name = ::FilterChipSample.name,
+        description = ChipsExampleDescription,
+        sourceUrl = ChipsExampleSourceUrl
+    ) {
+        FilterChipSample()
+    },
+    Example(
+        name = ::ElevatedFilterChipSample.name,
+        description = ChipsExampleDescription,
+        sourceUrl = ChipsExampleSourceUrl
+    ) {
+        ElevatedFilterChipSample()
+    },
+    Example(
+        name = ::FilterChipWithLeadingIconSample.name,
+        description = ChipsExampleDescription,
+        sourceUrl = ChipsExampleSourceUrl
+    ) {
+        FilterChipWithLeadingIconSample()
+    },
+    Example(
+        name = ::InputChipSample.name,
+        description = ChipsExampleDescription,
+        sourceUrl = ChipsExampleSourceUrl
+    ) {
+        InputChipSample()
+    },
+    Example(
+        name = ::InputChipWithAvatarSample.name,
+        description = ChipsExampleDescription,
+        sourceUrl = ChipsExampleSourceUrl
+    ) {
+        InputChipWithAvatarSample()
+    },
+    Example(
+        name = ::SuggestionChipSample.name,
+        description = ChipsExampleDescription,
+        sourceUrl = ChipsExampleSourceUrl
+    ) {
+        SuggestionChipSample()
+    },
+    Example(
+        name = ::ElevatedSuggestionChipSample.name,
+        description = ChipsExampleDescription,
+        sourceUrl = ChipsExampleSourceUrl
+    ) {
+        ElevatedSuggestionChipSample()
+    }
+)
+
 private const val DialogExampleDescription = "Dialog examples"
 private const val DialogExampleSourceUrl = "$SampleSourceUrl/AlertDialogSamples.kt"
 val DialogExamples =
@@ -561,6 +639,14 @@
     ) {
         SwitchSample()
     },
+
+    Example(
+        name = ::SwitchWithThumbIconSample.name,
+        description = SwitchExampleDescription,
+        sourceUrl = SwitchExampleSourceUrl
+    ) {
+        SwitchWithThumbIconSample()
+    },
 )
 
 private const val TabsExampleDescription = "Tabs examples"
diff --git a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/ChipSamples.kt b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/ChipSamples.kt
new file mode 100644
index 0000000..487a33b
--- /dev/null
+++ b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/ChipSamples.kt
@@ -0,0 +1,228 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material3.samples
+
+import androidx.annotation.Sampled
+import androidx.compose.foundation.horizontalScroll
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Close
+import androidx.compose.material.icons.filled.Done
+import androidx.compose.material.icons.filled.Home
+import androidx.compose.material.icons.filled.Person
+import androidx.compose.material.icons.filled.Settings
+import androidx.compose.material3.AssistChip
+import androidx.compose.material3.AssistChipDefaults
+import androidx.compose.material3.ElevatedAssistChip
+import androidx.compose.material3.ElevatedFilterChip
+import androidx.compose.material3.ElevatedSuggestionChip
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.FilterChip
+import androidx.compose.material3.FilterChipDefaults
+import androidx.compose.material3.Icon
+import androidx.compose.material3.InputChip
+import androidx.compose.material3.InputChipDefaults
+import androidx.compose.material3.SuggestionChip
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Sampled
+@Composable
+fun AssistChipSample() {
+    AssistChip(
+        onClick = { /* Do something! */ },
+        label = { Text("Assist Chip") },
+        leadingIcon = {
+            Icon(
+                Icons.Filled.Settings,
+                contentDescription = "Localized description",
+                Modifier.size(AssistChipDefaults.IconSize)
+            )
+        }
+    )
+}
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Sampled
+@Composable
+fun ElevatedAssistChipSample() {
+    ElevatedAssistChip(
+        onClick = { /* Do something! */ },
+        label = { Text("Assist Chip") },
+        leadingIcon = {
+            Icon(
+                Icons.Filled.Settings,
+                contentDescription = "Localized description",
+                Modifier.size(AssistChipDefaults.IconSize)
+            )
+        }
+    )
+}
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Sampled
+@Composable
+fun FilterChipSample() {
+    var selected by remember { mutableStateOf(false) }
+    FilterChip(
+        selected = selected,
+        onClick = { selected = !selected },
+        label = { Text("Filter chip") },
+        selectedIcon = {
+            Icon(
+                imageVector = Icons.Filled.Done,
+                contentDescription = "Localized Description",
+                modifier = Modifier.size(FilterChipDefaults.IconSize)
+            )
+        }
+    )
+}
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Sampled
+@Composable
+fun ElevatedFilterChipSample() {
+    var selected by remember { mutableStateOf(false) }
+    ElevatedFilterChip(
+        selected = selected,
+        onClick = { selected = !selected },
+        label = { Text("Filter chip") },
+        selectedIcon = {
+            Icon(
+                imageVector = Icons.Filled.Done,
+                contentDescription = "Localized Description",
+                modifier = Modifier.size(FilterChipDefaults.IconSize)
+            )
+        }
+    )
+}
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Sampled
+@Composable
+fun FilterChipWithLeadingIconSample() {
+    var selected by remember { mutableStateOf(false) }
+    FilterChip(
+        selected = selected,
+        onClick = { selected = !selected },
+        label = { Text("Filter chip") },
+        leadingIcon = {
+            Icon(
+                imageVector = Icons.Filled.Home,
+                contentDescription = "Localized description",
+                modifier = Modifier.size(FilterChipDefaults.IconSize)
+            )
+        },
+        selectedIcon = {
+            Icon(
+                imageVector = Icons.Filled.Done,
+                contentDescription = "Localized Description",
+                modifier = Modifier.size(FilterChipDefaults.IconSize)
+            )
+        }
+    )
+}
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Sampled
+@Composable
+fun InputChipSample() {
+    InputChip(
+        onClick = { /* Do something! */ },
+        label = { Text("Input Chip") },
+        trailingIcon = {
+            Icon(
+                Icons.Filled.Close,
+                contentDescription = "Localized description",
+                Modifier.size(InputChipDefaults.IconSize)
+            )
+        }
+    )
+}
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Sampled
+@Composable
+fun InputChipWithAvatarSample() {
+    InputChip(
+        onClick = { /* Do something! */ },
+        label = { Text("Input Chip") },
+        avatar = {
+            Icon(
+                Icons.Filled.Person,
+                contentDescription = "Localized description",
+                Modifier.size(InputChipDefaults.AvatarSize)
+            )
+        },
+        trailingIcon = {
+            Icon(
+                Icons.Filled.Close,
+                contentDescription = "Localized description",
+                Modifier.size(InputChipDefaults.IconSize)
+            )
+        }
+    )
+}
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Sampled
+@Composable
+fun SuggestionChipSample() {
+    SuggestionChip(
+        onClick = { /* Do something! */ },
+        label = { Text("Suggestion Chip") }
+    )
+}
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Sampled
+@Composable
+fun ElevatedSuggestionChipSample() {
+    ElevatedSuggestionChip(
+        onClick = { /* Do something! */ },
+        label = { Text("Suggestion Chip") }
+    )
+}
+
+@Sampled
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun ChipGroupSingleLineSample() {
+    Column(horizontalAlignment = Alignment.CenterHorizontally) {
+        Row(modifier = Modifier.horizontalScroll(rememberScrollState())) {
+            repeat(9) { index ->
+                InputChip(
+                    modifier = Modifier.padding(horizontal = 8.dp),
+                    onClick = { /* do something*/ },
+                    label = { Text("Chip $index") }
+                )
+            }
+        }
+    }
+}
diff --git a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/SwitchSample.kt b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/SwitchSample.kt
index 2ea2564..ab391ae 100644
--- a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/SwitchSample.kt
+++ b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/SwitchSample.kt
@@ -17,16 +17,41 @@
 package androidx.compose.material3.samples
 
 import androidx.annotation.Sampled
+import androidx.compose.foundation.layout.size
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Check
+import androidx.compose.material3.Icon
 import androidx.compose.material3.Switch
+import androidx.compose.material3.SwitchDefaults
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
 
 @Sampled
 @Composable
 fun SwitchSample() {
     var checked by remember { mutableStateOf(true) }
     Switch(checked = checked, onCheckedChange = { checked = it })
+}
+
+@Sampled
+@Composable
+fun SwitchWithThumbIconSample() {
+    var checked by remember { mutableStateOf(true) }
+    val icon: (@Composable () -> Unit)? = if (checked) {
+        {
+            Icon(
+                imageVector = Icons.Filled.Check,
+                contentDescription = null,
+                modifier = Modifier.size(SwitchDefaults.IconSize),
+            )
+        }
+    } else {
+        null
+    }
+
+    Switch(checked = checked, onCheckedChange = { checked = it }, thumbContent = icon)
 }
\ No newline at end of file
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/ChipScreenshotTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/ChipScreenshotTest.kt
new file mode 100644
index 0000000..23d66f4e
--- /dev/null
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/ChipScreenshotTest.kt
@@ -0,0 +1,486 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material3
+
+import android.os.Build
+import androidx.compose.foundation.layout.requiredSize
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Done
+import androidx.compose.material.icons.filled.Home
+import androidx.compose.material.icons.filled.Person
+import androidx.compose.material.icons.filled.Search
+import androidx.compose.testutils.assertAgainstGolden
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.captureToImage
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import androidx.test.filters.SdkSuppress
+import androidx.test.screenshot.AndroidXScreenshotTestRule
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+@OptIn(ExperimentalMaterial3Api::class)
+class ChipScreenshotTest {
+
+    @get:Rule
+    val rule = createComposeRule()
+
+    @get:Rule
+    val screenshotRule = AndroidXScreenshotTestRule(GOLDEN_MATERIAL3)
+
+    @Test
+    fun assistChip_flat_lightTheme() {
+        rule.setMaterialContent(lightColorScheme()) {
+            AssistChip(
+                onClick = {},
+                enabled = true,
+                modifier = Modifier.testTag(TestTag),
+                label = { Text("Assist chip") }
+            )
+        }
+        assertChipAgainstGolden("assistChip_flat_lightTheme")
+    }
+
+    @Test
+    fun assistChip_flat_darkTheme() {
+        rule.setMaterialContent(darkColorScheme()) {
+            AssistChip(
+                onClick = {},
+                enabled = true,
+                modifier = Modifier.testTag(TestTag),
+                label = { Text("Assist chip") }
+            )
+        }
+        assertChipAgainstGolden("assistChip_flat_darkTheme")
+    }
+
+    @Test
+    fun assistChip_elevated_lightTheme() {
+        rule.setMaterialContent(lightColorScheme()) {
+            ElevatedAssistChip(
+                onClick = {},
+                modifier = Modifier.testTag(TestTag),
+                label = { Text("Assist chip") }
+            )
+        }
+        assertChipAgainstGolden("assistChip_elevated_lightTheme")
+    }
+
+    @Test
+    fun assistChip_elevated_darkTheme() {
+        rule.setMaterialContent(darkColorScheme()) {
+            ElevatedAssistChip(
+                onClick = {},
+                modifier = Modifier.testTag(TestTag),
+                label = { Text("Assist chip") }
+            )
+        }
+        assertChipAgainstGolden("assistChip_elevated_darkTheme")
+    }
+
+    @Test
+    fun assistChip_flat_disabled_lightTheme() {
+        rule.setMaterialContent(lightColorScheme()) {
+            AssistChip(
+                onClick = {},
+                enabled = false,
+                modifier = Modifier.testTag(TestTag),
+                label = { Text("Assist chip") }
+            )
+        }
+        assertChipAgainstGolden("assistChip_flat_disabled_lightTheme")
+    }
+
+    @Test
+    fun assistChip_elevated_disabled_lightTheme() {
+        rule.setMaterialContent(lightColorScheme()) {
+            ElevatedAssistChip(
+                onClick = {},
+                modifier = Modifier.testTag(TestTag),
+                enabled = false,
+                label = { Text("Assist chip") }
+            )
+        }
+        assertChipAgainstGolden("assistChip_elevated_disabled_lightTheme")
+    }
+
+    @Test
+    fun inputChip_lightTheme() {
+        rule.setMaterialContent(lightColorScheme()) {
+            InputChip(
+                onClick = {},
+                enabled = true,
+                modifier = Modifier.testTag(TestTag),
+                label = { Text("Input chip") },
+                leadingIcon = {
+                    Icon(
+                        imageVector = Icons.Filled.Search,
+                        contentDescription = "Localized Description",
+                        modifier = Modifier.requiredSize(InputChipDefaults.IconSize)
+                    )
+                },
+                trailingIcon = {
+                    Icon(
+                        imageVector = Icons.Filled.Done,
+                        contentDescription = "Localized Description",
+                        modifier = Modifier.requiredSize(InputChipDefaults.IconSize)
+                    )
+                }
+            )
+        }
+        assertChipAgainstGolden("inputChip_lightTheme")
+    }
+
+    @Test
+    fun inputChip_withAvatar_lightTheme() {
+        rule.setMaterialContent(lightColorScheme()) {
+            InputChip(
+                onClick = {},
+                enabled = true,
+                modifier = Modifier.testTag(TestTag),
+                label = { Text("Input chip") },
+                avatar = {
+                    Icon(
+                        imageVector = Icons.Filled.Person,
+                        contentDescription = "Localized Description",
+                        modifier = Modifier.requiredSize(InputChipDefaults.AvatarSize)
+                    )
+                },
+                trailingIcon = {
+                    Icon(
+                        imageVector = Icons.Filled.Done,
+                        contentDescription = "Localized Description",
+                        modifier = Modifier.requiredSize(InputChipDefaults.IconSize)
+                    )
+                }
+            )
+        }
+        assertChipAgainstGolden("inputChip_withAvatar_lightTheme")
+    }
+
+    @Test
+    fun inputChip_darkTheme() {
+        rule.setMaterialContent(darkColorScheme()) {
+            InputChip(
+                onClick = {},
+                enabled = true,
+                modifier = Modifier.testTag(TestTag),
+                label = { Text("Input chip") },
+                leadingIcon = {
+                    Icon(
+                        imageVector = Icons.Filled.Search,
+                        contentDescription = "Localized Description",
+                        modifier = Modifier.requiredSize(InputChipDefaults.IconSize)
+                    )
+                },
+                trailingIcon = {
+                    Icon(
+                        imageVector = Icons.Filled.Done,
+                        contentDescription = "Localized Description",
+                        modifier = Modifier.requiredSize(InputChipDefaults.IconSize)
+                    )
+                }
+            )
+        }
+        assertChipAgainstGolden("inputChip_darkTheme")
+    }
+
+    @Test
+    fun inputChip_disabled_lightTheme() {
+        rule.setMaterialContent(darkColorScheme()) {
+            InputChip(
+                onClick = {},
+                enabled = false,
+                modifier = Modifier.testTag(TestTag),
+                label = { Text("Input chip") }
+            )
+        }
+        assertChipAgainstGolden("inputChip_disabled_lightTheme")
+    }
+
+    @Test
+    fun inputChip_disabled_darkTheme() {
+        rule.setMaterialContent(darkColorScheme()) {
+            InputChip(
+                onClick = {},
+                enabled = false,
+                modifier = Modifier.testTag(TestTag),
+                label = { Text("Input chip") }
+            )
+        }
+        assertChipAgainstGolden("inputChip_disabled_darkTheme")
+    }
+
+    @Test
+    fun filterChip_flat_selected_lightTheme() {
+        rule.setMaterialContent(lightColorScheme()) {
+            FilterChip(
+                selected = true,
+                onClick = {},
+                label = { Text("Filter Chip") },
+                modifier = Modifier.testTag(TestTag),
+                selectedIcon = {
+                    Icon(
+                        imageVector = Icons.Filled.Done,
+                        contentDescription = "Localized Description",
+                        modifier = Modifier.requiredSize(FilterChipDefaults.IconSize)
+                    )
+                }
+            )
+        }
+        assertChipAgainstGolden("filterChip_flat_selected_lightTheme")
+    }
+
+    @Test
+    fun filterChip_flat_withLeadingIcon_selected_lightTheme() {
+        rule.setMaterialContent(lightColorScheme()) {
+            FilterChip(
+                selected = true,
+                onClick = {},
+                label = { Text("Filter Chip") },
+                modifier = Modifier.testTag(TestTag),
+                leadingIcon = {
+                    Icon(
+                        Icons.Filled.Home,
+                        contentDescription = "Localized Description"
+                    )
+                },
+                selectedIcon = {
+                    Icon(
+                        imageVector = Icons.Filled.Done,
+                        contentDescription = "Localized Description",
+                        modifier = Modifier.requiredSize(FilterChipDefaults.IconSize)
+                    )
+                }
+            )
+        }
+        assertChipAgainstGolden("filterChip_flat_withLeadingIcon_selected_lightTheme")
+    }
+
+    @Test
+    fun filterChip_flat_withLeadingIcon_selected_darkTheme() {
+        rule.setMaterialContent(darkColorScheme()) {
+            FilterChip(
+                selected = true,
+                onClick = {},
+                label = { Text("Filter Chip") },
+                modifier = Modifier.testTag(TestTag),
+                leadingIcon = {
+                    Icon(
+                        Icons.Filled.Home,
+                        contentDescription = "Localized Description"
+                    )
+                },
+                selectedIcon = {
+                    Icon(
+                        imageVector = Icons.Filled.Done,
+                        contentDescription = "Localized Description",
+                        modifier = Modifier.requiredSize(FilterChipDefaults.IconSize)
+                    )
+                }
+            )
+        }
+        assertChipAgainstGolden("filterChip_flat_withLeadingIcon_selected_darkTheme")
+    }
+
+    @Test
+    fun filterChip_flat_notSelected() {
+        rule.setMaterialContent(lightColorScheme()) {
+            FilterChip(
+                selected = false,
+                onClick = {},
+                label = { Text("Filter Chip") },
+                modifier = Modifier.testTag(TestTag)
+            )
+        }
+        assertChipAgainstGolden("filterChip_flat_notSelected")
+    }
+
+    @Test
+    fun filterChip_flat_disabled_selected() {
+        rule.setMaterialContent(lightColorScheme()) {
+            FilterChip(
+                selected = true,
+                onClick = {},
+                label = { Text("Filter Chip") },
+                enabled = false,
+                modifier = Modifier.testTag(TestTag),
+                selectedIcon = {
+                    Icon(
+                        imageVector = Icons.Filled.Done,
+                        tint = LocalContentColor.current,
+                        contentDescription = "Localized Description",
+                        modifier = Modifier.requiredSize(FilterChipDefaults.IconSize)
+                    )
+                }
+            )
+        }
+        assertChipAgainstGolden("filterChip_flat_disabled_selected")
+    }
+
+    @Test
+    fun filterChip_flat_withLeadingIcon_disabled_selected() {
+        rule.setMaterialContent(lightColorScheme()) {
+            FilterChip(
+                selected = true,
+                onClick = {},
+                label = { Text("Filter Chip") },
+                enabled = false,
+                modifier = Modifier.testTag(TestTag),
+                leadingIcon = {
+                    Icon(
+                        Icons.Filled.Home,
+                        contentDescription = "Localized Description"
+                    )
+                },
+                selectedIcon = {
+                    Icon(
+                        imageVector = Icons.Filled.Done,
+                        contentDescription = "Localized Description",
+                        modifier = Modifier.requiredSize(FilterChipDefaults.IconSize)
+                    )
+                }
+            )
+        }
+        assertChipAgainstGolden("filterChip_flat_withLeadingIcon_disabled_selected")
+    }
+
+    @Test
+    fun filterChip_flat_disabled_notSelected() {
+        rule.setMaterialContent(lightColorScheme()) {
+            FilterChip(
+                selected = false,
+                onClick = {},
+                label = { Text("Filter Chip") },
+                enabled = false,
+                modifier = Modifier.testTag(TestTag)
+            )
+        }
+        assertChipAgainstGolden("filterChip_flat_disabled_notSelected")
+    }
+
+    @Test
+    fun filterChip_elevated_selected_darkTheme() {
+        rule.setMaterialContent(darkColorScheme()) {
+            FilterChip(
+                selected = true,
+                onClick = {},
+                label = { Text("Filter Chip") },
+                modifier = Modifier.testTag(TestTag),
+                selectedIcon = {
+                    Icon(
+                        imageVector = Icons.Filled.Done,
+                        contentDescription = "Localized Description",
+                        modifier = Modifier.requiredSize(FilterChipDefaults.IconSize)
+                    )
+                }
+            )
+        }
+        assertChipAgainstGolden("filterChip_elevated_selected_darkTheme")
+    }
+
+    @Test
+    fun suggestionChip_flat_lightTheme() {
+        rule.setMaterialContent(lightColorScheme()) {
+            SuggestionChip(
+                onClick = {},
+                enabled = true,
+                modifier = Modifier.testTag(TestTag),
+                label = { Text("Suggestion Chip") }
+            )
+        }
+        assertChipAgainstGolden("suggestionChip_flat_lightTheme")
+    }
+
+    @Test
+    fun suggestionChip_flat_darkTheme() {
+        rule.setMaterialContent(darkColorScheme()) {
+            SuggestionChip(
+                onClick = {},
+                enabled = true,
+                modifier = Modifier.testTag(TestTag),
+                label = { Text("Suggestion Chip") }
+            )
+        }
+        assertChipAgainstGolden("suggestionChip_flat_darkTheme")
+    }
+
+    @Test
+    fun suggestionChip_elevated_lightTheme() {
+        rule.setMaterialContent(lightColorScheme()) {
+            ElevatedSuggestionChip(
+                onClick = {},
+                modifier = Modifier.testTag(TestTag),
+                label = { Text("Suggestion Chip") }
+            )
+        }
+        assertChipAgainstGolden("suggestionChip_elevated_lightTheme")
+    }
+
+    @Test
+    fun suggestionChip_elevated_darkTheme() {
+        rule.setMaterialContent(darkColorScheme()) {
+            ElevatedSuggestionChip(
+                onClick = {},
+                modifier = Modifier.testTag(TestTag),
+                label = { Text("Suggestion Chip") }
+            )
+        }
+        assertChipAgainstGolden("suggestionChip_elevated_darkTheme")
+    }
+
+    @Test
+    fun suggestionChip_flat_disabled_lightTheme() {
+        rule.setMaterialContent(lightColorScheme()) {
+            SuggestionChip(
+                onClick = {},
+                enabled = false,
+                modifier = Modifier.testTag(TestTag),
+                label = { Text("Suggestion Chip") }
+            )
+        }
+        assertChipAgainstGolden("suggestionChip_flat_disabled_lightTheme")
+    }
+
+    @Test
+    fun suggestionChip_elevated_disabled_lightTheme() {
+        rule.setMaterialContent(lightColorScheme()) {
+            ElevatedSuggestionChip(
+                onClick = {},
+                modifier = Modifier.testTag(TestTag),
+                enabled = false,
+                label = { Text("Suggestion Chip") }
+            )
+        }
+        assertChipAgainstGolden("suggestionChip_elevated_disabled_lightTheme")
+    }
+
+    private fun assertChipAgainstGolden(goldenIdentifier: String) {
+        rule.onNodeWithTag(TestTag)
+            .captureToImage()
+            .assertAgainstGolden(screenshotRule, goldenIdentifier)
+    }
+}
+
+private const val TestTag = "chip"
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/ChipTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/ChipTest.kt
new file mode 100644
index 0000000..75f2ede
--- /dev/null
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/ChipTest.kt
@@ -0,0 +1,982 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material3
+
+import android.os.Build
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.requiredHeight
+import androidx.compose.foundation.layout.requiredSize
+import androidx.compose.foundation.layout.requiredWidth
+import androidx.compose.foundation.layout.size
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Done
+import androidx.compose.material.icons.filled.Person
+import androidx.compose.material3.tokens.AssistChipTokens
+import androidx.compose.material3.tokens.FilterChipTokens
+import androidx.compose.material3.tokens.InputChipTokens
+import androidx.compose.material3.tokens.SuggestionChipTokens
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.testutils.assertShape
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.Rect
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.RectangleShape
+import androidx.compose.ui.graphics.compositeOver
+import androidx.compose.ui.layout.LayoutCoordinates
+import androidx.compose.ui.layout.boundsInRoot
+import androidx.compose.ui.layout.boundsInWindow
+import androidx.compose.ui.layout.onGloballyPositioned
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.semantics.SemanticsProperties
+import androidx.compose.ui.test.SemanticsMatcher
+import androidx.compose.ui.test.assert
+import androidx.compose.ui.test.assertHasClickAction
+import androidx.compose.ui.test.assertHeightIsAtLeast
+import androidx.compose.ui.test.assertHeightIsEqualTo
+import androidx.compose.ui.test.assertIsEnabled
+import androidx.compose.ui.test.assertIsNotEnabled
+import androidx.compose.ui.test.assertIsNotSelected
+import androidx.compose.ui.test.assertIsSelected
+import androidx.compose.ui.test.assertLeftPositionInRootIsEqualTo
+import androidx.compose.ui.test.assertTouchHeightIsEqualTo
+import androidx.compose.ui.test.assertTouchWidthIsEqualTo
+import androidx.compose.ui.test.assertWidthIsEqualTo
+import androidx.compose.ui.test.captureToImage
+import androidx.compose.ui.test.click
+import androidx.compose.ui.test.hasClickAction
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.performClick
+import androidx.compose.ui.test.performTouchInput
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import androidx.test.filters.MediumTest
+import androidx.test.filters.SdkSuppress
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assume
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+@OptIn(ExperimentalMaterial3Api::class)
+class ChipTest {
+    @get:Rule
+    val rule = createComposeRule()
+
+    @Test
+    fun defaultSemantics_assistChip() {
+        rule.setMaterialContent(lightColorScheme()) {
+            Box {
+                AssistChip(
+                    onClick = {},
+                    modifier = Modifier.testTag(TestChipTag),
+                    label = { Text(TestChipTag) })
+            }
+        }
+
+        rule.onNodeWithTag(TestChipTag)
+            .assert(SemanticsMatcher.expectValue(SemanticsProperties.Role, Role.Button))
+            .assertIsEnabled()
+            .assertHasClickAction()
+    }
+
+    @Test
+    fun disabledSemantics_assistChip() {
+        rule.setMaterialContent(lightColorScheme()) {
+            Box {
+                AssistChip(
+                    modifier = Modifier.testTag(TestChipTag),
+                    onClick = {},
+                    label = { Text(TestChipTag) },
+                    enabled = false
+                )
+            }
+        }
+
+        rule.onNodeWithTag(TestChipTag)
+            .assert(SemanticsMatcher.expectValue(SemanticsProperties.Role, Role.Button))
+            .assertIsNotEnabled()
+            .assertHasClickAction()
+    }
+
+    @Test
+    fun onClick_assistChip() {
+        var counter = 0
+        val onClick: () -> Unit = { ++counter }
+
+        rule.setMaterialContent(lightColorScheme()) {
+            Box {
+                AssistChip(
+                    onClick = onClick,
+                    modifier = Modifier.testTag(TestChipTag),
+                    label = { Text("Test chip") })
+            }
+        }
+
+        rule.onNodeWithTag(TestChipTag)
+            .performClick()
+
+        rule.runOnIdle {
+            assertThat(counter).isEqualTo(1)
+        }
+    }
+
+    @Test
+    fun heightIsFromSpec_assistChip() {
+        // This test can be reasonable failing on the non default font scales
+        // so lets skip it.
+        Assume.assumeTrue(rule.density.fontScale <= 1f)
+        rule.setMaterialContent(lightColorScheme()) {
+            AssistChip(onClick = {}, label = { Text("Test chip") })
+        }
+
+        rule.onNode(hasClickAction())
+            .assertHeightIsEqualTo(AssistChipDefaults.Height)
+    }
+
+    @Test
+    fun horizontalPadding_assistChip() {
+        var chipCoordinates: LayoutCoordinates? = null
+        rule.setMaterialContent(lightColorScheme()) {
+            AssistChip(
+                onClick = {},
+                modifier = Modifier.onGloballyPositioned { chipCoordinates = it },
+                label = {
+                    Text(
+                        "Assist chip",
+                        Modifier.testTag(TestChipTag)
+                    )
+                })
+        }
+
+        var chipWidth = 0.dp
+        rule.runOnIdle {
+            chipWidth = with(rule.density) {
+                chipCoordinates!!.boundsInWindow().width.toDp()
+            }
+        }
+
+        rule.onNodeWithTag(TestChipTag, useUnmergedTree = true)
+            .assertLeftPositionInRootIsEqualTo(16.dp)
+            .assertWidthIsEqualTo(chipWidth - 16.dp - 16.dp)
+    }
+
+    @Test
+    fun horizontalPadding_assistChip_withLeadingIcon() {
+        var chipCoordinates: LayoutCoordinates? = null
+        rule.setMaterialContent(lightColorScheme()) {
+            AssistChip(
+                onClick = {},
+                modifier = Modifier.onGloballyPositioned { chipCoordinates = it },
+                label = {
+                    Text(
+                        "Assist chip",
+                        Modifier.testTag(TestChipTag)
+                    )
+                },
+                leadingIcon = {
+                    Icon(
+                        imageVector = Icons.Filled.Done,
+                        contentDescription = "Localized Description",
+                        modifier = Modifier.size(AssistChipDefaults.IconSize)
+                    )
+                })
+        }
+
+        var chipWidth = 0.dp
+        rule.runOnIdle {
+            chipWidth = with(rule.density) {
+                chipCoordinates!!.boundsInWindow().width.toDp()
+            }
+        }
+
+        rule.onNodeWithTag(TestChipTag, useUnmergedTree = true)
+            .assertLeftPositionInRootIsEqualTo(8.dp + AssistChipDefaults.IconSize + 8.dp)
+            .assertWidthIsEqualTo(chipWidth - 16.dp - AssistChipDefaults.IconSize - 16.dp)
+    }
+
+    @Test
+    fun labelContentColor_assistChip() {
+        var expectedLabelColor = Color.Unspecified
+        var contentColor = Color.Unspecified
+        rule.setMaterialContent(lightColorScheme()) {
+            expectedLabelColor = AssistChipTokens.LabelTextColor.toColor()
+            AssistChip(onClick = {}, label = {
+                contentColor = LocalContentColor.current
+            })
+        }
+
+        rule.runOnIdle {
+            assertThat(contentColor).isEqualTo(expectedLabelColor)
+        }
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+    @Test
+    @LargeTest
+    fun elevatedDisabled_assistChip() {
+        var containerColor = Color.Unspecified
+        rule.setMaterialContent(lightColorScheme()) {
+            containerColor = AssistChipTokens.ElevatedDisabledContainerColor.toColor()
+                .copy(alpha = AssistChipTokens.ElevatedDisabledContainerOpacity)
+                .compositeOver(MaterialTheme.colorScheme.surface)
+            ElevatedAssistChip(
+                modifier = Modifier.testTag(TestChipTag),
+                onClick = {},
+                label = {},
+                enabled = false,
+                shape = RectangleShape
+            )
+        }
+
+        rule.onNodeWithTag(TestChipTag)
+            .captureToImage()
+            .assertShape(
+                density = rule.density,
+                horizontalPadding = 0.dp,
+                verticalPadding = 0.dp,
+                backgroundColor = containerColor,
+                shapeColor = containerColor
+            )
+    }
+
+    @Test
+    fun unselectedSemantics_filterChip() {
+        rule.setMaterialContent(lightColorScheme()) {
+            Box {
+                FilterChip(
+                    selected = false,
+                    onClick = {},
+                    modifier = Modifier.testTag(TestChipTag),
+                    label = { Text(TestChipTag) })
+            }
+        }
+
+        rule.onNodeWithTag(TestChipTag)
+            .assert(SemanticsMatcher.expectValue(SemanticsProperties.Role, Role.Checkbox))
+            .assertIsEnabled()
+            .assertHasClickAction()
+            .assertIsNotSelected()
+    }
+
+    @Test
+    fun selectedSemantics_filterChip() {
+        rule.setMaterialContent(lightColorScheme()) {
+            Box {
+                FilterChip(
+                    selected = true,
+                    onClick = {},
+                    modifier = Modifier.testTag(TestChipTag),
+                    label = { Text(TestChipTag) })
+            }
+        }
+
+        rule.onNodeWithTag(TestChipTag)
+            .assert(SemanticsMatcher.expectValue(SemanticsProperties.Role, Role.Checkbox))
+            .assertIsEnabled()
+            .assertHasClickAction()
+            .assertIsSelected()
+    }
+
+    @Test
+    fun disabledSemantics_filterChip() {
+        rule.setMaterialContent(lightColorScheme()) {
+            Box {
+                FilterChip(
+                    selected = false,
+                    modifier = Modifier.testTag(TestChipTag),
+                    onClick = {},
+                    label = { Text(TestChipTag) },
+                    enabled = false
+                )
+            }
+        }
+
+        rule.onNodeWithTag(TestChipTag)
+            .assert(SemanticsMatcher.expectValue(SemanticsProperties.Role, Role.Checkbox))
+            .assertIsNotEnabled()
+            .assertHasClickAction()
+            .assertIsNotSelected()
+    }
+
+    @Test
+    fun toggle_filterChip() {
+        rule.setMaterialContent(lightColorScheme()) {
+            val selected = remember { mutableStateOf(false) }
+            Box {
+                FilterChip(
+                    selected = selected.value,
+                    onClick = { selected.value = !selected.value },
+                    modifier = Modifier.testTag(TestChipTag),
+                    label = { Text("Test chip") })
+            }
+        }
+
+        rule.onNodeWithTag(TestChipTag)
+            .assertIsNotSelected()
+            .performClick()
+            .assertIsSelected()
+            .performClick()
+            .assertIsNotSelected()
+    }
+
+    @Test
+    fun horizontalPadding_unselected_filterChip() {
+        var chipCoordinates: LayoutCoordinates? = null
+        rule.setMaterialContent(lightColorScheme()) {
+            FilterChip(
+                selected = false,
+                onClick = {},
+                modifier = Modifier.onGloballyPositioned { chipCoordinates = it },
+                label = {
+                    Text(
+                        "Filter chip",
+                        Modifier.testTag(TestChipTag)
+                    )
+                },
+                selectedIcon = {
+                    Icon(
+                        imageVector = Icons.Filled.Done,
+                        contentDescription = "Localized Description",
+                        modifier = Modifier.size(FilterChipDefaults.IconSize)
+                    )
+                })
+        }
+
+        var chipWidth = 0.dp
+        rule.runOnIdle {
+            chipWidth = with(rule.density) {
+                chipCoordinates!!.boundsInWindow().width.toDp()
+            }
+        }
+        rule.onNodeWithTag(TestChipTag, useUnmergedTree = true)
+            .assertLeftPositionInRootIsEqualTo(16.dp)
+            .assertWidthIsEqualTo(chipWidth - 16.dp - 16.dp)
+    }
+
+    @Test
+    fun horizontalPadding_selected_filterChip() {
+        var chipCoordinates: LayoutCoordinates? = null
+        rule.setMaterialContent(lightColorScheme()) {
+            FilterChip(
+                selected = true,
+                onClick = {},
+                modifier = Modifier.onGloballyPositioned { chipCoordinates = it },
+                label = {
+                    Text(
+                        "Filter chip",
+                        Modifier.testTag(TestChipTag)
+                    )
+                },
+                selectedIcon = {
+                    Icon(
+                        imageVector = Icons.Filled.Done,
+                        contentDescription = "Localized Description",
+                        modifier = Modifier.size(FilterChipDefaults.IconSize)
+                    )
+                })
+        }
+
+        var chipWidth = 0.dp
+        rule.runOnIdle {
+            chipWidth = with(rule.density) {
+                chipCoordinates!!.boundsInWindow().width.toDp()
+            }
+        }
+        rule.onNodeWithTag(TestChipTag, useUnmergedTree = true)
+            .assertLeftPositionInRootIsEqualTo(8.dp + FilterChipDefaults.IconSize + 8.dp)
+            .assertWidthIsEqualTo(chipWidth - 16.dp - FilterChipDefaults.IconSize - 16.dp)
+    }
+
+    @Test
+    fun horizontalPadding_filterChip_withIcons() {
+        var chipCoordinates: LayoutCoordinates? = null
+        rule.setMaterialContent(lightColorScheme()) {
+            FilterChip(
+                selected = false,
+                onClick = {},
+                modifier = Modifier.onGloballyPositioned { chipCoordinates = it },
+                label = {
+                    Text(
+                        "Filter chip",
+                        Modifier.testTag(TestChipTag)
+                    )
+                },
+                leadingIcon = {
+                    Icon(
+                        imageVector = Icons.Filled.Person,
+                        contentDescription = "Localized Description",
+                        modifier = Modifier.size(FilterChipDefaults.IconSize)
+                    )
+                },
+                trailingIcon = {
+                    Icon(
+                        imageVector = Icons.Filled.Done,
+                        contentDescription = "Localized Description",
+                        modifier = Modifier.size(FilterChipDefaults.IconSize)
+                    )
+                })
+        }
+
+        var chipWidth = 0.dp
+        rule.runOnIdle {
+            chipWidth = with(rule.density) {
+                chipCoordinates!!.boundsInWindow().width.toDp()
+            }
+        }
+        rule.onNodeWithTag(TestChipTag, useUnmergedTree = true)
+            .assertLeftPositionInRootIsEqualTo(8.dp + FilterChipDefaults.IconSize + 8.dp)
+            .assertWidthIsEqualTo(
+                chipWidth - 16.dp - FilterChipDefaults.IconSize -
+                    FilterChipDefaults.IconSize - 16.dp
+            )
+    }
+
+    @Test
+    fun heightIsFromSpec_filterChip() {
+        // This test can be reasonable failing on the non default font scales
+        // so lets skip it.
+        Assume.assumeTrue(rule.density.fontScale <= 1f)
+        rule.setMaterialContent(lightColorScheme()) {
+            FilterChip(selected = false, onClick = {}, label = { Text("Test chip") })
+        }
+
+        rule.onNode(hasClickAction())
+            .assertHeightIsEqualTo(FilterChipDefaults.Height)
+    }
+
+    @Test
+    fun labelContentColor_unselectedFilterChip() {
+        var expectedLabelColor = Color.Unspecified
+        var contentColor = Color.Unspecified
+        rule.setMaterialContent(lightColorScheme()) {
+            expectedLabelColor = FilterChipTokens.UnselectedLabelTextColor.toColor()
+            FilterChip(selected = false, onClick = {}, label = {
+                contentColor = LocalContentColor.current
+            })
+        }
+
+        rule.runOnIdle {
+            assertThat(contentColor).isEqualTo(expectedLabelColor)
+        }
+    }
+
+    @Test
+    fun labelContentColor_selectedFilterChip() {
+        var expectedLabelColor = Color.Unspecified
+        var contentColor = Color.Unspecified
+        rule.setMaterialContent(lightColorScheme()) {
+            expectedLabelColor = FilterChipTokens.SelectedLabelTextColor.toColor()
+            FilterChip(selected = true, onClick = {}, label = {
+                contentColor = LocalContentColor.current
+            })
+        }
+
+        rule.runOnIdle {
+            assertThat(contentColor).isEqualTo(expectedLabelColor)
+        }
+    }
+
+    @Test
+    fun defaultSemantics_inputChip() {
+        rule.setMaterialContent(lightColorScheme()) {
+            Box {
+                InputChip(
+                    onClick = {},
+                    modifier = Modifier.testTag(TestChipTag),
+                    label = { Text(TestChipTag) })
+            }
+        }
+
+        rule.onNodeWithTag(TestChipTag)
+            .assert(SemanticsMatcher.expectValue(SemanticsProperties.Role, Role.Button))
+            .assertIsEnabled()
+            .assertHasClickAction()
+    }
+
+    @Test
+    fun disabledSemantics_inputChip() {
+        rule.setMaterialContent(lightColorScheme()) {
+            Box {
+                InputChip(
+                    modifier = Modifier.testTag(TestChipTag),
+                    onClick = {},
+                    label = { Text(TestChipTag) },
+                    enabled = false
+                )
+            }
+        }
+
+        rule.onNodeWithTag(TestChipTag)
+            .assert(SemanticsMatcher.expectValue(SemanticsProperties.Role, Role.Button))
+            .assertIsNotEnabled()
+            .assertHasClickAction()
+    }
+
+    @Test
+    fun onClick_inputChip() {
+        var counter = 0
+        val onClick: () -> Unit = { ++counter }
+
+        rule.setMaterialContent(lightColorScheme()) {
+            Box {
+                InputChip(
+                    onClick = onClick,
+                    modifier = Modifier.testTag(TestChipTag),
+                    label = { Text("Test chip") })
+            }
+        }
+
+        rule.onNodeWithTag(TestChipTag)
+            .performClick()
+
+        rule.runOnIdle {
+            assertThat(counter).isEqualTo(1)
+        }
+    }
+
+    @Test
+    fun heightIsFromSpec_inputChip() {
+        // This test can be reasonable failing on the non default font scales
+        // so lets skip it.
+        Assume.assumeTrue(rule.density.fontScale <= 1f)
+        rule.setMaterialContent(lightColorScheme()) {
+            InputChip(onClick = {}, label = { Text("Test chip") })
+        }
+
+        rule.onNode(hasClickAction())
+            .assertHeightIsEqualTo(InputChipDefaults.Height)
+    }
+
+    @Test
+    fun horizontalPadding_inputChip() {
+        var chipCoordinates: LayoutCoordinates? = null
+        rule.setMaterialContent(lightColorScheme()) {
+            InputChip(
+                onClick = {},
+                modifier = Modifier.onGloballyPositioned { chipCoordinates = it },
+                label = {
+                    Text(
+                        "Input chip",
+                        Modifier.testTag(TestChipTag)
+                    )
+                })
+        }
+
+        var chipWidth = 0.dp
+        rule.runOnIdle {
+            chipWidth = with(rule.density) {
+                chipCoordinates!!.boundsInWindow().width.toDp()
+            }
+        }
+
+        rule.onNodeWithTag(TestChipTag, useUnmergedTree = true)
+            .assertLeftPositionInRootIsEqualTo(12.dp)
+            .assertWidthIsEqualTo(chipWidth - 12.dp - 12.dp)
+    }
+
+    @Test
+    fun horizontalPadding_inputChip_withLeadingIcon() {
+        var chipCoordinates: LayoutCoordinates? = null
+        rule.setMaterialContent(lightColorScheme()) {
+            InputChip(
+                onClick = {},
+                modifier = Modifier.onGloballyPositioned { chipCoordinates = it },
+                label = {
+                    Text(
+                        "Input chip",
+                        Modifier.testTag(TestChipTag)
+                    )
+                },
+                leadingIcon = {
+                    Icon(
+                        imageVector = Icons.Filled.Done,
+                        contentDescription = "Localized Description",
+                        modifier = Modifier.size(InputChipDefaults.IconSize)
+                    )
+                })
+        }
+
+        var chipWidth = 0.dp
+        rule.runOnIdle {
+            chipWidth = with(rule.density) {
+                chipCoordinates!!.boundsInWindow().width.toDp()
+            }
+        }
+
+        // Note that InputChip has slightly different padding than the other Chips.
+        rule.onNodeWithTag(TestChipTag, useUnmergedTree = true)
+            .assertLeftPositionInRootIsEqualTo(8.dp + InputChipDefaults.IconSize + 8.dp)
+            .assertWidthIsEqualTo(chipWidth - 16.dp - InputChipDefaults.IconSize - 12.dp)
+    }
+
+    @Test
+    fun horizontalPadding_inputChip_withAvatar() {
+        var chipCoordinates: LayoutCoordinates? = null
+        rule.setMaterialContent(lightColorScheme()) {
+            InputChip(
+                onClick = {},
+                modifier = Modifier.onGloballyPositioned { chipCoordinates = it },
+                label = {
+                    Text(
+                        "Input chip",
+                        Modifier.testTag(TestChipTag)
+                    )
+                },
+                avatar = {
+                    Icon(
+                        imageVector = Icons.Filled.Done,
+                        contentDescription = "Localized Description",
+                        modifier = Modifier.size(InputChipDefaults.AvatarSize)
+                    )
+                })
+        }
+
+        var chipWidth = 0.dp
+        rule.runOnIdle {
+            chipWidth = with(rule.density) {
+                chipCoordinates!!.boundsInWindow().width.toDp()
+            }
+        }
+
+        // Note that InputChip has slightly different padding than the other Chips.
+        rule.onNodeWithTag(TestChipTag, useUnmergedTree = true)
+            .assertLeftPositionInRootIsEqualTo(4.dp + InputChipDefaults.AvatarSize + 8.dp)
+            .assertWidthIsEqualTo(chipWidth - 12.dp - InputChipDefaults.AvatarSize - 12.dp)
+    }
+
+    @Test
+    fun labelContentColor_inputChip() {
+        var expectedLabelColor = Color.Unspecified
+        var contentColor = Color.Unspecified
+        rule.setMaterialContent(lightColorScheme()) {
+            expectedLabelColor = InputChipTokens.LabelTextColor.toColor()
+            InputChip(onClick = {}, label = {
+                contentColor = LocalContentColor.current
+            })
+        }
+
+        rule.runOnIdle {
+            assertThat(contentColor).isEqualTo(expectedLabelColor)
+        }
+    }
+
+    @Test
+    fun defaultSemantics_suggestionChip() {
+        rule.setMaterialContent(lightColorScheme()) {
+            Box {
+                SuggestionChip(
+                    onClick = {},
+                    modifier = Modifier.testTag(TestChipTag),
+                    label = { Text(TestChipTag) })
+            }
+        }
+
+        rule.onNodeWithTag(TestChipTag)
+            .assert(SemanticsMatcher.expectValue(SemanticsProperties.Role, Role.Button))
+            .assertIsEnabled()
+            .assertHasClickAction()
+    }
+
+    @Test
+    fun disabledSemantics_suggestionChip() {
+        rule.setMaterialContent(lightColorScheme()) {
+            Box {
+                SuggestionChip(
+                    modifier = Modifier.testTag(TestChipTag),
+                    onClick = {},
+                    label = { Text(TestChipTag) },
+                    enabled = false
+                )
+            }
+        }
+
+        rule.onNodeWithTag(TestChipTag)
+            .assert(SemanticsMatcher.expectValue(SemanticsProperties.Role, Role.Button))
+            .assertIsNotEnabled()
+            .assertHasClickAction()
+    }
+
+    @Test
+    fun onClick_suggestionChip() {
+        var counter = 0
+        val onClick: () -> Unit = { ++counter }
+
+        rule.setMaterialContent(lightColorScheme()) {
+            Box {
+                SuggestionChip(
+                    onClick = onClick,
+                    modifier = Modifier.testTag(TestChipTag),
+                    label = { Text("Test chip") })
+            }
+        }
+
+        rule.onNodeWithTag(TestChipTag)
+            .performClick()
+
+        rule.runOnIdle {
+            assertThat(counter).isEqualTo(1)
+        }
+    }
+
+    @Test
+    fun heightIsFromSpec_suggestionChip() {
+        // This test can be reasonable failing on the non default font scales
+        // so lets skip it.
+        Assume.assumeTrue(rule.density.fontScale <= 1f)
+        rule.setMaterialContent(lightColorScheme()) {
+            SuggestionChip(onClick = {}, label = { Text("Test chip") })
+        }
+
+        rule.onNode(hasClickAction())
+            .assertHeightIsEqualTo(SuggestionChipDefaults.Height)
+    }
+
+    @Test
+    fun horizontalPadding_suggestionChip() {
+        var chipCoordinates: LayoutCoordinates? = null
+        rule.setMaterialContent(lightColorScheme()) {
+            SuggestionChip(
+                onClick = {},
+                modifier = Modifier.onGloballyPositioned { chipCoordinates = it },
+                label = {
+                    Text(
+                        "Suggestion chip",
+                        Modifier.testTag(TestChipTag)
+                    )
+                })
+        }
+
+        var chipWidth = 0.dp
+        rule.runOnIdle {
+            chipWidth = with(rule.density) {
+                chipCoordinates!!.boundsInWindow().width.toDp()
+            }
+        }
+
+        rule.onNodeWithTag(TestChipTag, useUnmergedTree = true)
+            .assertLeftPositionInRootIsEqualTo(16.dp)
+            .assertWidthIsEqualTo(chipWidth - 16.dp - 16.dp)
+    }
+
+    @Test
+    fun horizontalPadding_suggestionChip_withLeadingIcon() {
+        var chipCoordinates: LayoutCoordinates? = null
+        rule.setMaterialContent(lightColorScheme()) {
+            SuggestionChip(
+                onClick = {},
+                modifier = Modifier.onGloballyPositioned { chipCoordinates = it },
+                label = {
+                    Text(
+                        "Suggestion chip",
+                        Modifier.testTag(TestChipTag)
+                    )
+                },
+                icon = {
+                    Icon(
+                        imageVector = Icons.Filled.Done,
+                        contentDescription = "Localized Description",
+                        modifier = Modifier.size(SuggestionChipDefaults.IconSize)
+                    )
+                })
+        }
+
+        var chipWidth = 0.dp
+        rule.runOnIdle {
+            chipWidth = with(rule.density) {
+                chipCoordinates!!.boundsInWindow().width.toDp()
+            }
+        }
+
+        rule.onNodeWithTag(TestChipTag, useUnmergedTree = true)
+            .assertLeftPositionInRootIsEqualTo(8.dp + SuggestionChipDefaults.IconSize + 8.dp)
+            .assertWidthIsEqualTo(chipWidth - 16.dp - SuggestionChipDefaults.IconSize - 16.dp)
+    }
+
+    @Test
+    fun labelContentColor_suggestionChip() {
+        var expectedLabelColor = Color.Unspecified
+        var contentColor = Color.Unspecified
+        rule.setMaterialContent(lightColorScheme()) {
+            expectedLabelColor = SuggestionChipTokens.LabelTextColor.toColor()
+            SuggestionChip(onClick = {}, label = {
+                contentColor = LocalContentColor.current
+            })
+        }
+
+        rule.runOnIdle {
+            assertThat(contentColor).isEqualTo(expectedLabelColor)
+        }
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+    @Test
+    @LargeTest
+    fun elevatedDisabled_suggestionChip() {
+        var containerColor = Color.Unspecified
+        rule.setMaterialContent(lightColorScheme()) {
+            containerColor = SuggestionChipTokens.ElevatedDisabledContainerColor.toColor()
+                .copy(alpha = SuggestionChipTokens.ElevatedDisabledContainerOpacity)
+                .compositeOver(MaterialTheme.colorScheme.surface)
+            ElevatedSuggestionChip(
+                modifier = Modifier.testTag(TestChipTag),
+                onClick = {},
+                label = {},
+                enabled = false,
+                shape = RectangleShape
+            )
+        }
+
+        rule.onNodeWithTag(TestChipTag)
+            .captureToImage()
+            .assertShape(
+                density = rule.density,
+                horizontalPadding = 0.dp,
+                verticalPadding = 0.dp,
+                backgroundColor = containerColor,
+                shapeColor = containerColor
+            )
+    }
+
+    @Test
+    fun canBeDisabled() {
+        rule.setMaterialContent(lightColorScheme()) {
+            var enabled by remember { mutableStateOf(true) }
+            val onClick = { enabled = false }
+            Box {
+                SuggestionChip(
+                    modifier = Modifier.testTag(TestChipTag),
+                    onClick = onClick,
+                    label = { Text("Hello") },
+                    enabled = enabled
+                )
+            }
+        }
+        rule.onNodeWithTag(TestChipTag)
+            // Confirm the chip starts off enabled, with a click action
+            .assertHasClickAction()
+            .assertIsEnabled()
+            .performClick()
+            // Then confirm it's disabled with click action after clicking it
+            .assertHasClickAction()
+            .assertIsNotEnabled()
+    }
+
+    @Test
+    fun withLargeFontSizeIsLargerThenHeight() {
+        rule.setMaterialContent(lightColorScheme()) {
+            SuggestionChip(onClick = {}, label = {
+                Text(
+                    text = "Test chip",
+                    fontSize = 50.sp
+                )
+            })
+        }
+
+        rule.onNode(hasClickAction())
+            .assertHeightIsAtLeast(SuggestionChipDefaults.Height + 1.dp)
+    }
+
+    @Test
+    fun propagateDefaultTextStyle() {
+        var textStyle: TextStyle? = null
+        var body2TextStyle: TextStyle? = null
+        rule.setMaterialContent(lightColorScheme()) {
+            SuggestionChip(onClick = {}, label = {
+                textStyle = LocalTextStyle.current
+                body2TextStyle =
+                    MaterialTheme.typography.fromToken(SuggestionChipTokens.LabelTextFont)
+            })
+        }
+
+        rule.runOnIdle { assertThat(textStyle).isEqualTo(body2TextStyle) }
+    }
+
+    @Test
+    fun contentIsRow() {
+        var chipBounds = Rect(0f, 0f, 0f, 0f)
+        var item1Bounds = Rect(0f, 0f, 0f, 0f)
+        var item2Bounds = Rect(0f, 0f, 0f, 0f)
+        rule.setMaterialContent(lightColorScheme()) {
+            SuggestionChip(
+                onClick = {},
+                modifier = Modifier.onGloballyPositioned {
+                    chipBounds = it.boundsInRoot()
+                },
+                label = {
+                    Spacer(
+                        Modifier.requiredSize(10.dp).onGloballyPositioned {
+                            item1Bounds = it.boundsInRoot()
+                        }
+                    )
+                    Spacer(
+                        Modifier.requiredWidth(10.dp).requiredHeight(5.dp)
+                            .onGloballyPositioned {
+                                item2Bounds = it.boundsInRoot()
+                            }
+                    )
+                }
+            )
+        }
+
+        assertThat(item1Bounds.center.y).isWithin(1f).of(chipBounds.center.y)
+        assertThat(item2Bounds.center.y).isWithin(1f).of(chipBounds.center.y)
+        assertThat(item1Bounds.right).isWithin(1f).of(chipBounds.center.x)
+        assertThat(item2Bounds.left).isWithin(1f).of(chipBounds.center.x)
+    }
+
+    @Test
+    fun clickableInMinimumTouchTarget() {
+        var clicked = false
+        rule.setMaterialContent(lightColorScheme()) {
+            Box(Modifier.fillMaxSize()) {
+                SuggestionChip(
+                    modifier = Modifier.align(Alignment.Center).testTag(TestChipTag)
+                        .requiredSize(10.dp),
+                    onClick = { clicked = !clicked },
+                    label = { Box(Modifier.size(10.dp)) }
+                )
+            }
+        }
+
+        rule.onNodeWithTag(TestChipTag)
+            .assertWidthIsEqualTo(10.dp)
+            .assertHeightIsEqualTo(10.dp)
+            .assertTouchWidthIsEqualTo(48.dp)
+            .assertTouchHeightIsEqualTo(48.dp)
+            .performTouchInput {
+                click(Offset(-1f, -1f))
+            }
+
+        assertThat(clicked).isTrue()
+    }
+}
+
+private const val TestChipTag = "chip"
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/SwitchScreenshotTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/SwitchScreenshotTest.kt
index c8346a7..8a7c1bf 100644
--- a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/SwitchScreenshotTest.kt
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/SwitchScreenshotTest.kt
@@ -18,7 +18,12 @@
 
 import android.os.Build
 import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.layout.wrapContentSize
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Check
+import androidx.compose.material.icons.filled.Close
+import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
@@ -266,6 +271,50 @@
         assertToggeableAgainstGolden("switch_focus")
     }
 
+    @Test
+    fun switchTest_checked_icon() {
+        rule.setMaterialContent(lightColorScheme()) {
+            val icon: @Composable () -> Unit = {
+                Icon(
+                    imageVector = Icons.Filled.Check,
+                    contentDescription = null,
+                    modifier = Modifier.size(SwitchDefaults.IconSize),
+                )
+            }
+            Box(wrapperModifier) {
+                Switch(
+                    checked = true,
+                    onCheckedChange = { },
+                    thumbContent = icon
+                )
+            }
+        }
+
+        assertToggeableAgainstGolden("switch_checked_icon")
+    }
+
+    @Test
+    fun switchTest_unchecked_icon() {
+        rule.setMaterialContent(lightColorScheme()) {
+            val icon: @Composable () -> Unit = {
+                Icon(
+                    imageVector = Icons.Filled.Close,
+                    contentDescription = null,
+                    modifier = Modifier.size(SwitchDefaults.IconSize),
+                )
+            }
+            Box(wrapperModifier) {
+                Switch(
+                    checked = false,
+                    onCheckedChange = { },
+                    thumbContent = icon
+                )
+            }
+        }
+
+        assertToggeableAgainstGolden("switch_unchecked_icon")
+    }
+
     private fun assertToggeableAgainstGolden(goldenName: String) {
         rule.onNodeWithTag(wrapperTestTag)
             .captureToImage()
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/SwitchTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/SwitchTest.kt
index 3d44ccc..6c607be 100644
--- a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/SwitchTest.kt
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/SwitchTest.kt
@@ -40,12 +40,14 @@
 import androidx.compose.ui.test.assert
 import androidx.compose.ui.test.assertHasClickAction
 import androidx.compose.ui.test.assertHasNoClickAction
+import androidx.compose.ui.test.assertHeightIsAtLeast
 import androidx.compose.ui.test.assertHeightIsEqualTo
 import androidx.compose.ui.test.assertIsEnabled
 import androidx.compose.ui.test.assertIsOff
 import androidx.compose.ui.test.assertIsOn
 import androidx.compose.ui.test.assertTouchHeightIsEqualTo
 import androidx.compose.ui.test.assertTouchWidthIsEqualTo
+import androidx.compose.ui.test.assertWidthIsAtLeast
 import androidx.compose.ui.test.assertWidthIsEqualTo
 import androidx.compose.ui.test.click
 import androidx.compose.ui.test.isFocusable
@@ -336,17 +338,11 @@
             }
         }.run {
             if (clickable && minimumTouchTarget) {
-                assertIsSquareWithSize(48.dp)
+                assertWidthIsAtLeast(48.dp)
+                assertHeightIsAtLeast(48.dp)
             } else {
-                // The padding should be 2 DP, but we round to pixels when determining layout
-                val paddingInPixels = 2.dp.roundToPx()
-
-                // Convert back to DP so that we have an exact DP value to work with. We don't
-                // want to multiply the error by two (one for each padding), so we get the exact
-                // padding based on the expected pixels consumed by the padding.
-                val paddingInDp = paddingInPixels.toDp()
-                assertWidthIsEqualTo(SwitchTokens.TrackWidth + paddingInDp * 2)
-                assertHeightIsEqualTo(SwitchTokens.HandleHeight + paddingInDp * 2)
+                assertWidthIsEqualTo(SwitchTokens.TrackWidth)
+                assertHeightIsEqualTo(SwitchTokens.TrackHeight)
             }
         }
     }
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Chip.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Chip.kt
new file mode 100644
index 0000000..2bbf677
--- /dev/null
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Chip.kt
@@ -0,0 +1,2270 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material3
+
+import androidx.compose.animation.core.Animatable
+import androidx.compose.animation.core.VectorConverter
+import androidx.compose.foundation.BorderStroke
+import androidx.compose.foundation.interaction.DragInteraction
+import androidx.compose.foundation.interaction.FocusInteraction
+import androidx.compose.foundation.interaction.HoverInteraction
+import androidx.compose.foundation.interaction.Interaction
+import androidx.compose.foundation.interaction.InteractionSource
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.interaction.PressInteraction
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.RowScope
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.defaultMinSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import androidx.compose.material3.tokens.AssistChipTokens
+import androidx.compose.material3.tokens.FilterChipTokens
+import androidx.compose.material3.tokens.InputChipTokens
+import androidx.compose.material3.tokens.SuggestionChipTokens
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.Immutable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.Stable
+import androidx.compose.runtime.State
+import androidx.compose.runtime.mutableStateListOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberUpdatedState
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.alpha
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.semantics.role
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+
+/**
+ * <a href="https://m3.material.io/components/chips/overview" class="external" target="_blank">Material Design assist chip</a>.
+ *
+ * Chips help people enter information, make selections, filter content, or trigger actions. Chips
+ * can show multiple interactive elements together in the same area, such as a list of selectable
+ * movie times, or a series of email contacts.
+ *
+ * Assist chips represent smart or automated actions that can span multiple apps, such as opening a
+ * calendar event from the home screen. Assist chips function as though the user asked an assistant
+ * to complete the action. They should appear dynamically and contextually in a UI.
+ *
+ * ![Assist chip image](https://developer.android.com/images/reference/androidx/compose/material3/assist-chip.png)
+ *
+ * This assist chip is applied with a flat style. If you want an elevated style, use the
+ * [ElevatedAssistChip].
+ *
+ * Example of a flat AssistChip:
+ * @sample androidx.compose.material3.samples.AssistChipSample
+ *
+ * @param onClick Called when the chip is clicked. If null, then this chip will be considered
+ * read-only unless something else handles its input events and updates its state.
+ * @param label A text label for this chip
+ * @param modifier Modifier to be applied to the chip
+ * @param enabled When disabled, chip will not respond to user input. It will also appear visually
+ * disabled and disabled to accessibility services.
+ * @param leadingIcon Optional icon at the start of the chip, preceding the [label] text.
+ * @param trailingIcon Optional icon at the end of the chip.
+ * @param interactionSource The [MutableInteractionSource] represents the stream of [Interaction]s
+ * for this chip. You can create and pass in your own remembered [MutableInteractionSource] if you
+ * want to observe [Interaction]s and customize the appearance / behavior of this chip in
+ * different [Interaction]s
+ * @param elevation [ChipElevation] used to resolve the elevation for this chip. The resolved value
+ * controls the size of the shadow below the chip, as well as its tonal elevation. When
+ * chip's container color is [ColorScheme.surface], a higher tonal elevation value will result in a
+ * darker chip color in light theme and lighter color in dark theme. See also [Surface] and
+ * [AssistChipDefaults.assistChipElevation].
+ * @param shape Defines the chip's shape as well its shadow. A shadow is only displayed if the
+ * [elevation] is resolved to be greater than zero.
+ * @param border [ChipBorder] that will be used to resolve the border to draw around the chip. Pass
+ * `null` here for no border. See [AssistChipDefaults.assistChipBorder].
+ * @param colors [ChipColors] that will be used to resolve the container and content color for this
+ * chip in different states. See [AssistChipDefaults.assistChipColors].
+ */
+@ExperimentalMaterial3Api
+@Composable
+fun AssistChip(
+    onClick: () -> Unit,
+    label: @Composable RowScope.() -> Unit,
+    modifier: Modifier = Modifier,
+    enabled: Boolean = true,
+    leadingIcon: @Composable (() -> Unit)? = null,
+    trailingIcon: @Composable (() -> Unit)? = null,
+    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
+    elevation: ChipElevation? = AssistChipDefaults.assistChipElevation(),
+    shape: Shape = AssistChipTokens.ContainerShape.toShape(),
+    border: ChipBorder? = AssistChipDefaults.assistChipBorder(),
+    colors: ChipColors = AssistChipDefaults.assistChipColors()
+) = Chip(
+    modifier = modifier,
+    onClick = onClick,
+    enabled = enabled,
+    label = label,
+    labelTextStyle = MaterialTheme.typography.fromToken(AssistChipTokens.LabelTextFont),
+    labelColor = colors.labelColor(enabled).value,
+    leadingIcon = leadingIcon,
+    avatar = null,
+    trailingIcon = trailingIcon,
+    leadingIconColor = colors.leadingIconContentColor(enabled).value,
+    trailingIconColor = colors.trailingIconContentColor(enabled).value,
+    containerColor = colors.containerColor(enabled).value,
+    tonalElevation = elevation?.tonalElevation(enabled, interactionSource)?.value ?: 0.dp,
+    shadowElevation = elevation?.shadowElevation(enabled, interactionSource)?.value ?: 0.dp,
+    minHeight = AssistChipDefaults.Height,
+    paddingValues = AssistChipPadding,
+    shape = shape,
+    border = border?.borderStroke(enabled)?.value,
+    interactionSource = interactionSource
+)
+
+/**
+ * <a href="https://m3.material.io/components/chips/overview" class="external" target="_blank">Material Design elevated assist chip</a>.
+ *
+ * Chips help people enter information, make selections, filter content, or trigger actions. Chips
+ * can show multiple interactive elements together in the same area, such as a list of selectable
+ * movie times, or a series of email contacts.
+ *
+ * Assist chips represent smart or automated actions that can span multiple apps, such as opening a
+ * calendar event from the home screen. Assist chips function as though the user asked an assistant
+ * to complete the action. They should appear dynamically and contextually in a UI.
+ *
+ * ![Assist chip image](https://developer.android.com/images/reference/androidx/compose/material3/assist-chip.png)
+ *
+ * This assist chip is applied with an elevated style. If you want a flat style, use the
+ * [AssistChip].
+ *
+ * Example of an elevated AssistChip with a trailing icon:
+ * @sample androidx.compose.material3.samples.ElevatedAssistChipSample
+ *
+ * @param onClick Called when the chip is clicked. If null, then this chip will be considered
+ * read-only unless something else handles its input events and updates its state.
+ * @param label A text label for this chip
+ * @param modifier Modifier to be applied to the chip
+ * @param enabled When disabled, chip will not respond to user input. It will also appear visually
+ * disabled and disabled to accessibility services.
+ * @param leadingIcon Optional icon at the start of the chip, preceding the [label] text.
+ * @param trailingIcon Optional icon at the end of the chip.
+ * @param interactionSource The [MutableInteractionSource] represents the stream of [Interaction]s
+ * for this chip. You can create and pass in your own remembered [MutableInteractionSource] if you
+ * want to observe [Interaction]s and customize the appearance / behavior of this chip in
+ * different [Interaction]s
+ * @param elevation [ChipElevation] used to resolve the elevation for this chip. The resolved value
+ * controls the size of the shadow below the chip, as well as its tonal elevation. When
+ * chip's container color is [ColorScheme.surface], a higher tonal elevation value will result in a
+ * darker chip color in light theme and lighter color in dark theme. See also [Surface] and
+ * [AssistChipDefaults.elevatedAssistChipElevation].
+ * @param shape Defines the chip's shape as well its shadow. A shadow is only displayed if the
+ * [elevation] is resolved to be greater than zero.
+ * @param border [ChipBorder] that will be used to resolve the border to draw around the chip. Pass
+ * `null` here for no border.
+ * @param colors [ChipColors] that will be used to resolve the container and content color for this
+ * chip in different states. See [AssistChipDefaults.elevatedAssistChipColors].
+ */
+@ExperimentalMaterial3Api
+@Composable
+fun ElevatedAssistChip(
+    onClick: () -> Unit,
+    label: @Composable RowScope.() -> Unit,
+    modifier: Modifier = Modifier,
+    enabled: Boolean = true,
+    leadingIcon: @Composable (() -> Unit)? = null,
+    trailingIcon: @Composable (() -> Unit)? = null,
+    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
+    elevation: ChipElevation? = AssistChipDefaults.elevatedAssistChipElevation(),
+    shape: Shape = AssistChipTokens.ContainerShape.toShape(),
+    border: ChipBorder? = null,
+    colors: ChipColors = AssistChipDefaults.elevatedAssistChipColors()
+) = Chip(
+    modifier = modifier,
+    onClick = onClick,
+    enabled = enabled,
+    label = label,
+    labelTextStyle = MaterialTheme.typography.fromToken(AssistChipTokens.LabelTextFont),
+    labelColor = colors.labelColor(enabled).value,
+    leadingIcon = leadingIcon,
+    avatar = null,
+    trailingIcon = trailingIcon,
+    leadingIconColor = colors.leadingIconContentColor(enabled).value,
+    trailingIconColor = colors.trailingIconContentColor(enabled).value,
+    containerColor = colors.containerColor(enabled).value,
+    tonalElevation = elevation?.tonalElevation(enabled, interactionSource)?.value ?: 0.dp,
+    shadowElevation = elevation?.shadowElevation(enabled, interactionSource)?.value ?: 0.dp,
+    minHeight = AssistChipDefaults.Height,
+    paddingValues = AssistChipPadding,
+    shape = shape,
+    border = border?.borderStroke(enabled)?.value,
+    interactionSource = interactionSource
+)
+
+/**
+ * <a href="https://m3.material.io/components/chips/overview" class="external" target="_blank">Material Design filter chip</a>.
+ *
+ * Chips help people enter information, make selections, filter content, or trigger actions. Chips
+ * can show multiple interactive elements together in the same area, such as a list of selectable
+ * movie times, or a series of email contacts.
+ *
+ * Filter chips use tags or descriptive words to filter content. They can be a good alternative to
+ * toggle buttons or checkboxes.
+ *
+ * ![Filter chip image](https://developer.android.com/images/reference/androidx/compose/material3/filter-chip.png)
+ *
+ * This filter chip is applied with a flat style. If you want an elevated style, use the
+ * [ElevatedFilterChip].
+ *
+ * Tapping on a filter chip selects it, and in case a [selectedIcon] is provided (e.g. a checkmark),
+ * it's appended to the starting edge of the chip's label, drawn instead of any given [leadingIcon].
+ *
+ * Example of a flat FilterChip with a trailing icon:
+ * @sample androidx.compose.material3.samples.FilterChipSample
+ *
+ * Example of a FilterChip with both a leading icon and a selected icon:
+ * @sample androidx.compose.material3.samples.FilterChipWithLeadingIconSample
+ *
+ * @param selected Whether the chip is selected
+ * @param onClick Called when the chip is clicked. If null, then this chip will be considered
+ * read-only unless something else handles its input events and updates its state.
+ * @param label A text label for this chip
+ * @param modifier Modifier to be applied to the chip
+ * @param enabled When disabled, chip will not respond to user input. It will also appear visually
+ * disabled and disabled to accessibility services.
+ * @param leadingIcon Optional icon at the start of the chip, preceding the [label] text.
+ * @param selectedIcon Optional icon at the start of the chip, preceding the [label] text, that will
+ * de displayed when the chip is selected, instead of any given [leadingIcon].
+ * @param trailingIcon Optional icon at the end of the chip.
+ * @param interactionSource The [MutableInteractionSource] represents the stream of [Interaction]s
+ * for this chip. You can create and pass in your own remembered [MutableInteractionSource] if you
+ * want to observe [Interaction]s and customize the appearance / behavior of this chip in
+ * different [Interaction]s
+ * @param elevation [SelectableChipElevation] used to resolve the elevation for this chip. The
+ * resolved value controls the size of the shadow below the chip, as well as its tonal elevation.
+ * When chip's container color is [ColorScheme.surface], a higher tonal elevation value will result
+ * in a darker chip color in light theme and lighter color in dark theme. See also [Surface] and
+ * [FilterChipDefaults.filterChipElevation].
+ * @param shape Defines the chip's shape as well its shadow. A shadow is only displayed if the
+ * [elevation] is resolved to be greater than zero.
+ * @param border [SelectableChipBorder] that will be used to resolve the border to draw around the
+ * chip. Pass `null` here for no border. See [FilterChipDefaults.filterChipBorder].
+ * @param colors [SelectableChipColors] that will be used to resolve the container and content color
+ * for this chip in different states. See [FilterChipDefaults.filterChipColors].
+ */
+@ExperimentalMaterial3Api
+@Composable
+fun FilterChip(
+    selected: Boolean,
+    onClick: () -> Unit,
+    label: @Composable RowScope.() -> Unit,
+    modifier: Modifier = Modifier,
+    enabled: Boolean = true,
+    leadingIcon: @Composable (() -> Unit)? = null,
+    selectedIcon: @Composable (() -> Unit)? = null,
+    trailingIcon: @Composable (() -> Unit)? = null,
+    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
+    elevation: SelectableChipElevation? = FilterChipDefaults.filterChipElevation(),
+    shape: Shape = FilterChipTokens.ContainerShape.toShape(),
+    border: SelectableChipBorder? = FilterChipDefaults.filterChipBorder(),
+    colors: SelectableChipColors = FilterChipDefaults.filterChipColors()
+) = SelectableChip(
+    selected = selected,
+    modifier = modifier,
+    onClick = onClick,
+    enabled = enabled,
+    label = label,
+    labelTextStyle = MaterialTheme.typography.fromToken(FilterChipTokens.LabelTextFont),
+    leadingIcon = if (selected) selectedIcon else leadingIcon,
+    trailingIcon = trailingIcon,
+    elevation = elevation,
+    colors = colors,
+    minHeight = FilterChipDefaults.Height,
+    paddingValues = FilterChipPadding,
+    shape = shape,
+    border = border?.borderStroke(enabled, selected)?.value,
+    interactionSource = interactionSource
+)
+
+/**
+ * <a href="https://m3.material.io/components/chips/overview" class="external" target="_blank">Material Design elevated filter chip</a>.
+ *
+ * Chips help people enter information, make selections, filter content, or trigger actions. Chips
+ * can show multiple interactive elements together in the same area, such as a list of selectable
+ * movie times, or a series of email contacts.
+ *
+ * Filter chips use tags or descriptive words to filter content. They can be a good alternative to
+ * toggle buttons or checkboxes.
+ *
+ * ![Filter chip image](https://developer.android.com/images/reference/androidx/compose/material3/filter-chip.png)
+ *
+ * This filter chip is applied with an elevated style. If you want a flat style, use the
+ * [FilterChip].
+ *
+ * Tapping on a filter chip selects it, and in case a [selectedIcon] is provided (e.g. a checkmark),
+ * it's appended to the starting edge of the chip's label, drawn instead of any given [leadingIcon].
+ *
+ * Example of an elevated FilterChip with a trailing icon:
+ * @sample androidx.compose.material3.samples.ElevatedFilterChipSample
+ *
+ * @param selected Whether the chip is selected
+ * @param onClick Called when the chip is clicked. If null, then this chip will be considered
+ * read-only unless something else handles its input events and updates its state.
+ * @param label A text label for this chip
+ * @param modifier Modifier to be applied to the chip
+ * @param enabled When disabled, chip will not respond to user input. It will also appear visually
+ * disabled and disabled to accessibility services.
+ * @param leadingIcon Optional icon at the start of the chip, preceding the [label] text.
+ * @param selectedIcon Optional icon at the start of the chip, preceding the [label] text, that will
+ * de displayed when the chip is selected, instead of any given [leadingIcon].
+ * @param trailingIcon Optional icon at the end of the chip.
+ * @param interactionSource The [MutableInteractionSource] represents the stream of [Interaction]s
+ * for this chip. You can create and pass in your own remembered [MutableInteractionSource] if you
+ * want to observe [Interaction]s and customize the appearance / behavior of this chip in
+ * different [Interaction]s
+ * @param elevation [SelectableChipElevation] used to resolve the elevation for this chip. The
+ * resolved value controls the size of the shadow below the chip, as well as its tonal elevation.
+ * When chip's container color is [ColorScheme.surface], a higher tonal elevation value will result
+ * in a darker chip color in light theme and lighter color in dark theme. See also [Surface] and
+ * [FilterChipDefaults.elevatedFilterChipElevation].
+ * @param shape Defines the chip's shape as well its shadow. A shadow is only displayed if the
+ * [elevation] is resolved to be greater than zero.
+ * @param border [SelectableChipBorder] that will be used to resolve the border to draw around the
+ * chip. Pass `null` here for no border.
+ * @param colors [SelectableChipColors] that will be used to resolve the container and content color
+ * for this chip in different states. See [FilterChipDefaults.elevatedFilterChipColors].
+ */
+@ExperimentalMaterial3Api
+@Composable
+fun ElevatedFilterChip(
+    selected: Boolean,
+    onClick: () -> Unit,
+    label: @Composable RowScope.() -> Unit,
+    modifier: Modifier = Modifier,
+    enabled: Boolean = true,
+    leadingIcon: @Composable (() -> Unit)? = null,
+    selectedIcon: @Composable (() -> Unit)? = null,
+    trailingIcon: @Composable (() -> Unit)? = null,
+    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
+    elevation: SelectableChipElevation? = FilterChipDefaults.elevatedFilterChipElevation(),
+    shape: Shape = FilterChipTokens.ContainerShape.toShape(),
+    border: SelectableChipBorder? = null,
+    colors: SelectableChipColors = FilterChipDefaults.elevatedFilterChipColors()
+) = SelectableChip(
+    selected = selected,
+    modifier = modifier,
+    onClick = onClick,
+    enabled = enabled,
+    label = label,
+    labelTextStyle = MaterialTheme.typography.fromToken(FilterChipTokens.LabelTextFont),
+    leadingIcon = if (selected) selectedIcon else leadingIcon,
+    trailingIcon = trailingIcon,
+    elevation = elevation,
+    colors = colors,
+    minHeight = FilterChipDefaults.Height,
+    paddingValues = FilterChipPadding,
+    shape = shape,
+    border = border?.borderStroke(enabled, selected)?.value,
+    interactionSource = interactionSource
+)
+
+/**
+ * <a href="https://m3.material.io/components/chips/overview" class="external" target="_blank">Material Design input chip</a>.
+ *
+ * Chips help people enter information, make selections, filter content, or trigger actions. Chips
+ * can show multiple interactive elements together in the same area, such as a list of selectable
+ * movie times, or a series of email contacts.
+ *
+ * Input chips represent discrete pieces of information entered by a user.
+ *
+ * ![Input chip image](https://developer.android.com/images/reference/androidx/compose/material3/input-chip.png)
+ *
+ * An Input Chip can have a leading icon or an avatar at its start. In case both are provided, the
+ * avatar will take precedence and will be displayed.
+ *
+ * Example of an InputChip with a trailing icon:
+ * @sample androidx.compose.material3.samples.InputChipSample
+ *
+ * Example of an InputChip with an avatar and a trailing icon:
+ * @sample androidx.compose.material3.samples.InputChipWithAvatarSample
+ *
+ * Input chips should appear in a set and can be horizontally scrollable:
+ * @sample androidx.compose.material3.samples.ChipGroupSingleLineSample
+ *
+ * Alternatively, use Accompanist's [Flow Layouts](https://google.github.io/accompanist/flowlayout/)
+ * to wrap chips to a new line.
+ *
+ * @param onClick Called when the chip is clicked. If null, then this chip will be considered
+ * read-only unless something else handles its input events and updates its state.
+ * @param label A text label for this chip
+ * @param modifier Modifier to be applied to the chip
+ * @param enabled When disabled, chip will not respond to user input. It will also appear visually
+ * disabled and disabled to accessibility services.
+ * @param leadingIcon Optional icon at the start of the chip, preceding the [label] text.
+ * @param avatar Optional avatar at the start of the chip, preceding the [label] text.
+ * @param trailingIcon Optional icon at the end of the chip.
+ * @param interactionSource The [MutableInteractionSource] represents the stream of [Interaction]s
+ * for this chip. You can create and pass in your own remembered [MutableInteractionSource] if you
+ * want to observe [Interaction]s and customize the appearance / behavior of this chip in
+ * different [Interaction]s
+ * @param elevation [ChipElevation] used to resolve the elevation for this chip. The resolved value
+ * controls the size of the shadow below the chip, as well as its tonal elevation. When
+ * chip's container color is [ColorScheme.surface], a higher tonal elevation value will result in a
+ * darker chip color in light theme and lighter color in dark theme. See also [Surface] and
+ * [InputChipDefaults.inputChipElevation].
+ * @param shape Defines the chip's shape as well its shadow. A shadow is only displayed if the
+ * [elevation] is resolved to be greater than zero.
+ * @param border [ChipBorder] that will be used to resolve the border to draw around the chip. Pass
+ * `null` here for no border. See [InputChipDefaults.inputChipBorder]
+ * @param colors [ChipColors] that will be used to resolve the container and content color for this
+ * chip in different states. See [InputChipDefaults.inputChipColors].
+ */
+@ExperimentalMaterial3Api
+@Composable
+fun InputChip(
+    onClick: () -> Unit,
+    label: @Composable RowScope.() -> Unit,
+    modifier: Modifier = Modifier,
+    enabled: Boolean = true,
+    leadingIcon: @Composable (() -> Unit)? = null,
+    avatar: @Composable (() -> Unit)? = null,
+    trailingIcon: @Composable (() -> Unit)? = null,
+    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
+    elevation: ChipElevation? = InputChipDefaults.inputChipElevation(),
+    shape: Shape = InputChipTokens.ContainerShape.toShape(),
+    border: ChipBorder? = InputChipDefaults.inputChipBorder(),
+    colors: ChipColors = InputChipDefaults.inputChipColors()
+) {
+    // If given, place the avatar in an InputChipTokens.AvatarShape shape before passing it into the
+    // Chip function.
+    var shapedAvatar: @Composable (() -> Unit)? = null
+    if (avatar != null) {
+        val avatarOpacity = if (enabled) 1f else InputChipTokens.DisabledAvatarOpacity
+        shapedAvatar = @Composable {
+            Box(
+                modifier = Modifier
+                    .alpha(avatarOpacity)
+                    .clip(InputChipTokens.AvatarShape.toShape()),
+                contentAlignment = Alignment.Center
+            ) {
+                avatar()
+            }
+        }
+    }
+    Chip(
+        modifier = modifier,
+        onClick = onClick,
+        enabled = enabled,
+        label = label,
+        labelTextStyle = MaterialTheme.typography.fromToken(InputChipTokens.LabelTextFont),
+        labelColor = colors.labelColor(enabled).value,
+        leadingIcon = leadingIcon,
+        avatar = shapedAvatar,
+        trailingIcon = trailingIcon,
+        leadingIconColor = colors.leadingIconContentColor(enabled).value,
+        trailingIconColor = colors.trailingIconContentColor(enabled).value,
+        containerColor = colors.containerColor(enabled).value,
+        tonalElevation = elevation?.tonalElevation(enabled, interactionSource)?.value ?: 0.dp,
+        shadowElevation = elevation?.shadowElevation(enabled, interactionSource)?.value ?: 0.dp,
+        minHeight = InputChipDefaults.Height,
+        paddingValues = inputChipPadding(
+            hasAvatar = shapedAvatar != null,
+            hasLeadingIcon = leadingIcon != null,
+            hasTrailingIcon = trailingIcon != null
+        ),
+        shape = shape,
+        border = border?.borderStroke(enabled)?.value,
+        interactionSource = interactionSource
+    )
+}
+
+/**
+ * <a href="https://m3.material.io/components/chips/overview" class="external" target="_blank">Material Design suggestion chip</a>.
+ *
+ * Chips help people enter information, make selections, filter content, or trigger actions. Chips
+ * can show multiple interactive elements together in the same area, such as a list of selectable
+ * movie times, or a series of email contacts.
+ *
+ * Suggestion chips help narrow a user's intent by presenting dynamically generated suggestions,
+ * such as possible responses or search filters.
+ *
+ * ![Suggestion chip image](https://developer.android.com/images/reference/androidx/compose/material3/suggestion-chip.png)
+ *
+ * This suggestion chip is applied with a flat style. If you want an elevated style, use the
+ * [ElevatedSuggestionChip].
+ *
+ * Example of a flat SuggestionChip with a trailing icon:
+ * @sample androidx.compose.material3.samples.SuggestionChipSample
+ *
+ * @param onClick Called when the chip is clicked. If null, then this chip will be considered
+ * read-only unless something else handles its input events and updates its state.
+ * @param label A text label for this chip
+ * @param modifier Modifier to be applied to the chip
+ * @param enabled When disabled, chip will not respond to user input. It will also appear visually
+ * disabled and disabled to accessibility services.
+ * @param icon Optional icon at the start of the chip, preceding the [label] text.
+ * @param interactionSource The [MutableInteractionSource] represents the stream of [Interaction]s
+ * for this chip. You can create and pass in your own remembered [MutableInteractionSource] if you
+ * want to observe [Interaction]s and customize the appearance / behavior of this chip in
+ * different [Interaction]s
+ * @param elevation [ChipElevation] used to resolve the elevation for this chip. The resolved value
+ * controls the size of the shadow below the chip, as well as its tonal elevation. When
+ * chip's container color is [ColorScheme.surface], a higher tonal elevation value will result in a
+ * darker chip color in light theme and lighter color in dark theme. See also [Surface] and
+ * [SuggestionChipDefaults.suggestionChipElevation].
+ * @param shape Defines the chip's shape as well its shadow. A shadow is only displayed if the
+ * [elevation] is resolved to be greater than zero.
+ * @param border [ChipBorder] that will be used to resolve the border to draw around the chip. Pass
+ * `null` here for no border. See [SuggestionChipDefaults.suggestionChipBorder].
+ * @param colors [ChipColors] that will be used to resolve the container and content color for this
+ * chip in different states. See [SuggestionChipDefaults.suggestionChipColors].
+ */
+@ExperimentalMaterial3Api
+@Composable
+fun SuggestionChip(
+    onClick: () -> Unit,
+    label: @Composable RowScope.() -> Unit,
+    modifier: Modifier = Modifier,
+    enabled: Boolean = true,
+    icon: @Composable (() -> Unit)? = null,
+    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
+    elevation: ChipElevation? = SuggestionChipDefaults.suggestionChipElevation(),
+    shape: Shape = SuggestionChipTokens.ContainerShape.toShape(),
+    border: ChipBorder? = SuggestionChipDefaults.suggestionChipBorder(),
+    colors: ChipColors = SuggestionChipDefaults.suggestionChipColors()
+) = Chip(
+    modifier = modifier,
+    onClick = onClick,
+    enabled = enabled,
+    label = label,
+    labelTextStyle = MaterialTheme.typography.fromToken(SuggestionChipTokens.LabelTextFont),
+    labelColor = colors.labelColor(enabled).value,
+    leadingIcon = icon,
+    avatar = null,
+    trailingIcon = null,
+    leadingIconColor = colors.leadingIconContentColor(enabled).value,
+    trailingIconColor = colors.trailingIconContentColor(enabled).value,
+    containerColor = colors.containerColor(enabled).value,
+    tonalElevation = elevation?.tonalElevation(enabled, interactionSource)?.value ?: 0.dp,
+    shadowElevation = elevation?.shadowElevation(enabled, interactionSource)?.value ?: 0.dp,
+    minHeight = SuggestionChipDefaults.Height,
+    paddingValues = SuggestionChipPadding,
+    shape = shape,
+    border = border?.borderStroke(enabled)?.value,
+    interactionSource = interactionSource
+)
+
+/**
+ * <a href="https://m3.material.io/components/chips/overview" class="external" target="_blank">Material Design elevated suggestion chip</a>.
+ *
+ * Chips help people enter information, make selections, filter content, or trigger actions. Chips
+ * can show multiple interactive elements together in the same area, such as a list of selectable
+ * movie times, or a series of email contacts.
+ *
+ * Suggestion chips help narrow a user's intent by presenting dynamically generated suggestions,
+ * such as possible responses or search filters.
+ *
+ * ![Suggestion chip image](https://developer.android.com/images/reference/androidx/compose/material3/suggestion-chip.png)
+ *
+ * This suggestion chip is applied with an elevated style. If you want a flat style, use the
+ * [SuggestionChip].
+ *
+ * Example of an elevated SuggestionChip with a trailing icon:
+ * @sample androidx.compose.material3.samples.ElevatedSuggestionChipSample
+ *
+ * @param onClick Called when the chip is clicked. If null, then this chip will be considered
+ * read-only unless something else handles its input events and updates its state.
+ * @param label A text label for this chip
+ * @param modifier Modifier to be applied to the chip
+ * @param enabled When disabled, chip will not respond to user input. It will also appear visually
+ * disabled and disabled to accessibility services.
+ * @param icon Optional icon at the start of the chip, preceding the [label] text.
+ * @param interactionSource The [MutableInteractionSource] represents the stream of [Interaction]s
+ * for this chip. You can create and pass in your own remembered [MutableInteractionSource] if you
+ * want to observe [Interaction]s and customize the appearance / behavior of this chip in
+ * different [Interaction]s
+ * @param elevation [ChipElevation] used to resolve the elevation for this chip. The resolved value
+ * controls the size of the shadow below the chip, as well as its tonal elevation. When
+ * chip's container color is [ColorScheme.surface], a higher tonal elevation value will result in a
+ * darker chip color in light theme and lighter color in dark theme. See also [Surface],
+ * [SuggestionChipDefaults.suggestionChipElevation], and
+ * [SuggestionChipDefaults.elevatedSuggestionChipElevation].
+ * @param shape Defines the chip's shape as well its shadow. A shadow is only displayed if the
+ * [elevation] is resolved to be greater than zero.
+ * @param border [ChipBorder] that will be used to resolve the border to draw around the chip. Pass
+ * `null` here for no border. See [SuggestionChipDefaults.suggestionChipBorder].
+ * @param colors [ChipColors] that will be used to resolve the container and content color for this
+ * chip in different states. See [SuggestionChipDefaults.suggestionChipColors] and
+ * [SuggestionChipDefaults.elevatedSuggestionChipColors].
+ */
+@ExperimentalMaterial3Api
+@Composable
+fun ElevatedSuggestionChip(
+    onClick: () -> Unit,
+    label: @Composable RowScope.() -> Unit,
+    modifier: Modifier = Modifier,
+    enabled: Boolean = true,
+    icon: @Composable (() -> Unit)? = null,
+    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
+    elevation: ChipElevation? = SuggestionChipDefaults.elevatedSuggestionChipElevation(),
+    shape: Shape = SuggestionChipTokens.ContainerShape.toShape(),
+    border: ChipBorder? = null,
+    colors: ChipColors = SuggestionChipDefaults.elevatedSuggestionChipColors()
+) = Chip(
+    modifier = modifier,
+    onClick = onClick,
+    enabled = enabled,
+    label = label,
+    labelTextStyle = MaterialTheme.typography.fromToken(SuggestionChipTokens.LabelTextFont),
+    labelColor = colors.labelColor(enabled).value,
+    leadingIcon = icon,
+    avatar = null,
+    trailingIcon = null,
+    leadingIconColor = colors.leadingIconContentColor(enabled).value,
+    trailingIconColor = colors.trailingIconContentColor(enabled).value,
+    containerColor = colors.containerColor(enabled).value,
+    tonalElevation = elevation?.tonalElevation(enabled, interactionSource)?.value ?: 0.dp,
+    shadowElevation = elevation?.shadowElevation(enabled, interactionSource)?.value ?: 0.dp,
+    minHeight = SuggestionChipDefaults.Height,
+    paddingValues = SuggestionChipPadding,
+    shape = shape,
+    border = border?.borderStroke(enabled)?.value,
+    interactionSource = interactionSource
+)
+
+/**
+ * Represents the elevation for a chip in different states.
+ */
+@Stable
+@ExperimentalMaterial3Api
+interface ChipElevation {
+    /**
+     * Represents the tonal elevation used in a chip, depending on [enabled] and
+     * [interactionSource]. This should typically be the same value as the [shadowElevation].
+     *
+     * Tonal elevation is used to apply a color shift to the surface to give the it higher emphasis.
+     * When surface's color is [ColorScheme.surface], a higher the elevation will result
+     * in a darker color in light theme and lighter color in dark theme.
+     *
+     * See [shadowElevation] which controls the elevation of the shadow drawn around the Chip.
+     *
+     * @param enabled whether the chip is enabled
+     * @param interactionSource the [InteractionSource] for this chip
+     */
+    @Composable
+    fun tonalElevation(enabled: Boolean, interactionSource: InteractionSource): State<Dp>
+
+    /**
+     * Represents the shadow elevation used in a chip, depending on [enabled] and
+     * [interactionSource]. This should typically be the same value as the [tonalElevation].
+     *
+     * Shadow elevation is used to apply a shadow around the surface to give it higher emphasis.
+     *
+     * See [tonalElevation] which controls the elevation with a color shift to the surface.
+     *
+     * @param enabled whether the chip is enabled
+     * @param interactionSource the [InteractionSource] for this chip
+     */
+    @Composable
+    fun shadowElevation(enabled: Boolean, interactionSource: InteractionSource): State<Dp>
+}
+
+/**
+ * Represents the elevation used in a selectable chip in different states.
+ */
+@Stable
+@ExperimentalMaterial3Api
+interface SelectableChipElevation {
+    /**
+     * Represents the tonal elevation used in a chip, depending on [enabled], [selected], and
+     * [interactionSource]. This should typically be the same value as the [shadowElevation].
+     *
+     * Tonal elevation is used to apply a color shift to the surface to give the it higher emphasis.
+     * When surface's color is [ColorScheme.surface], a higher the elevation will result
+     * in a darker color in light theme and lighter color in dark theme.
+     *
+     * See [shadowElevation] which controls the elevation of the shadow drawn around the Chip.
+     *
+     * @param enabled whether the chip is enabled
+     * @param selected whether the chip is selected
+     * @param interactionSource the [InteractionSource] for this chip
+     */
+    @Composable
+    fun tonalElevation(
+        enabled: Boolean,
+        selected: Boolean,
+        interactionSource: InteractionSource
+    ): State<Dp>
+
+    /**
+     * Represents the shadow elevation used in a chip, depending on [enabled], [selected], and
+     * [interactionSource]. This should typically be the same value as the [tonalElevation].
+     *
+     * Shadow elevation is used to apply a shadow around the surface to give it higher emphasis.
+     *
+     * See [tonalElevation] which controls the elevation with a color shift to the surface.
+     *
+     * @param enabled whether the chip is enabled
+     * @param selected whether the chip is selected
+     * @param interactionSource the [InteractionSource] for this chip
+     */
+    @Composable
+    fun shadowElevation(
+        enabled: Boolean,
+        selected: Boolean,
+        interactionSource: InteractionSource
+    ): State<Dp>
+}
+
+/**
+ * Represents the container and content colors used in a clickable chip in different states.
+ *
+ * See [AssistChipDefaults], [InputChipDefaults], and [SuggestionChipDefaults] for the default
+ * colors used in the various Chip configurations.
+ */
+@Stable
+@ExperimentalMaterial3Api
+interface ChipColors {
+    /**
+     * Represents the container  color for this chip, depending on [enabled].
+     *
+     * @param enabled whether the chip is enabled
+     */
+    @Composable
+    fun containerColor(enabled: Boolean): State<Color>
+
+    /**
+     * Represents the label color for this chip, depending on [enabled].
+     *
+     * @param enabled whether the chip is enabled
+     */
+    @Composable
+    fun labelColor(enabled: Boolean): State<Color>
+
+    /**
+     * Represents the leading icon's content color for this chip, depending on [enabled].
+     *
+     * @param enabled whether the chip is enabled
+     */
+    @Composable
+    fun leadingIconContentColor(enabled: Boolean): State<Color>
+
+    /**
+     * Represents the trailing icon's content color for this chip, depending on [enabled].
+     *
+     * @param enabled whether the chip is enabled
+     */
+    @Composable
+    fun trailingIconContentColor(enabled: Boolean): State<Color>
+}
+
+/**
+ * Represents the container  and content colors used in a selectable chip in different states.
+ *
+ * See [FilterChipDefaults.filterChipColors] and [FilterChipDefaults.elevatedFilterChipColors] for
+ * the default colors used in [FilterChip].
+ */
+@Stable
+@ExperimentalMaterial3Api
+interface SelectableChipColors {
+    /**
+     * Represents the container  color for this chip, depending on [enabled] and [selected].
+     *
+     * @param enabled whether the chip is enabled
+     * @param selected whether the chip is selected
+     */
+    @Composable
+    fun containerColor(enabled: Boolean, selected: Boolean): State<Color>
+
+    /**
+     * Represents the label color for this chip, depending on [enabled] and [selected].
+     *
+     * @param enabled whether the chip is enabled
+     * @param selected whether the chip is selected
+     */
+    @Composable
+    fun labelColor(enabled: Boolean, selected: Boolean): State<Color>
+
+    /**
+     * Represents the leading icon color for this chip, depending on [enabled] and [selected].
+     *
+     * @param enabled whether the chip is enabled
+     * @param selected whether the chip is selected
+     */
+    @Composable
+    fun leadingIconContentColor(enabled: Boolean, selected: Boolean): State<Color>
+
+    /**
+     * Represents the trailing icon color for this chip, depending on [enabled] and [selected].
+     *
+     * @param enabled whether the chip is enabled
+     * @param selected whether the chip is selected
+     */
+    @Composable
+    fun trailingIconContentColor(enabled: Boolean, selected: Boolean): State<Color>
+}
+
+/**
+ * Represents the border stroke used in a chip in different states.
+ */
+@Stable
+@ExperimentalMaterial3Api
+interface ChipBorder {
+
+    /**
+     * Represents the [BorderStroke] for this chip, depending on [enabled].
+     *
+     * @param enabled whether the chip is enabled
+     */
+    @Composable
+    fun borderStroke(enabled: Boolean): State<BorderStroke?>
+}
+
+/**
+ * Represents the border stroke used used in a selectable chip in different states.
+ */
+@Stable
+@ExperimentalMaterial3Api
+interface SelectableChipBorder {
+
+    /**
+     * Represents the [BorderStroke] stroke used for this chip, depending on [enabled] and
+     * [selected].
+     *
+     * @param enabled whether the chip is enabled
+     * @param selected whether the chip is selected
+     */
+    @Composable
+    fun borderStroke(enabled: Boolean, selected: Boolean): State<BorderStroke>
+}
+
+/**
+ * Contains the baseline values used by [AssistChip].
+ */
+@ExperimentalMaterial3Api
+object AssistChipDefaults {
+    /**
+     * The height applied for an assist chip.
+     * Note that you can override it by applying Modifier.height directly on a chip.
+     */
+    val Height = AssistChipTokens.ContainerHeight
+
+    /**
+     * The size of an assist chip icon.
+     */
+    val IconSize = AssistChipTokens.IconSize
+
+    /**
+     * Creates a [ChipColors] that represents the default container , label, and icon colors used in
+     * a flat [AssistChip].
+     *
+     * @param containerColor the container color of this chip when enabled
+     * @param labelColor the label color of this chip when enabled
+     * @param leadingIconContentColor the color of this chip's start icon when enabled
+     * @param trailingIconContentColor the color of this chip's end icon when enabled
+     * @param disabledContainerColor the container  color of this chip when not enabled
+     * @param disabledLabelColor the label color of this chip when not enabled
+     * @param disabledLeadingIconContentColor the color of this chip's start icon when not enabled
+     * @param disabledTrailingIconContentColor the color of this chip's end icon when not enabled
+     */
+    @Composable
+    fun assistChipColors(
+        containerColor: Color = Color.Transparent,
+        labelColor: Color = AssistChipTokens.LabelTextColor.toColor(),
+        leadingIconContentColor: Color = AssistChipTokens.IconColor.toColor(),
+        trailingIconContentColor: Color = leadingIconContentColor,
+        disabledContainerColor: Color = Color.Transparent,
+        disabledLabelColor: Color = AssistChipTokens.DisabledLabelTextColor.toColor()
+            .copy(alpha = AssistChipTokens.DisabledLabelTextOpacity),
+        disabledLeadingIconContentColor: Color =
+            AssistChipTokens.DisabledIconColor.toColor()
+                .copy(alpha = AssistChipTokens.DisabledIconOpacity),
+        disabledTrailingIconContentColor: Color = disabledLeadingIconContentColor,
+    ): ChipColors = DefaultChipColors(
+        containerColor = containerColor,
+        labelColor = labelColor,
+        leadingIconContentColor = leadingIconContentColor,
+        trailingIconContentColor = trailingIconContentColor,
+        disabledContainerColor = disabledContainerColor,
+        disabledLabelColor = disabledLabelColor,
+        disabledLeadingIconContentColor = disabledLeadingIconContentColor,
+        disabledTrailingIconContentColor = disabledTrailingIconContentColor
+    )
+
+    /**
+     * Creates a [ChipElevation] that will animate between the provided values according to the
+     * Material specification for a flat [AssistChip].
+     *
+     * @param defaultElevation the elevation used when the [AssistChip] is has no other
+     * [Interaction]s
+     * @param pressedElevation the elevation used when the chip is pressed.
+     * @param focusedElevation the elevation used when the chip is focused
+     * @param hoveredElevation the elevation used when the chip is hovered
+     * @param draggedElevation the elevation used when the chip is dragged
+     * @param disabledElevation the elevation used when the chip is not enabled
+     */
+    @Composable
+    fun assistChipElevation(
+        defaultElevation: Dp = AssistChipTokens.FlatContainerElevation,
+        pressedElevation: Dp = defaultElevation,
+        focusedElevation: Dp = defaultElevation,
+        hoveredElevation: Dp = defaultElevation,
+        draggedElevation: Dp = AssistChipTokens.DraggedContainerElevation,
+        disabledElevation: Dp = defaultElevation
+    ): ChipElevation {
+        return remember(
+            defaultElevation,
+            pressedElevation,
+            focusedElevation,
+            hoveredElevation,
+            draggedElevation,
+            disabledElevation
+        ) {
+            DefaultChipElevation(
+                defaultElevation = defaultElevation,
+                pressedElevation = pressedElevation,
+                focusedElevation = focusedElevation,
+                hoveredElevation = hoveredElevation,
+                draggedElevation = draggedElevation,
+                disabledElevation = disabledElevation
+            )
+        }
+    }
+
+    /**
+     * Creates a [ChipBorder] that represents the default border used in a flat [AssistChip].
+     *
+     * @param borderColor the border color of this chip when enabled
+     * @param disabledBorderColor the border color of this chip when not enabled
+     * @param borderWidth the border stroke width of this chip
+     */
+    @Composable
+    fun assistChipBorder(
+        borderColor: Color = AssistChipTokens.FlatOutlineColor.toColor(),
+        disabledBorderColor: Color = AssistChipTokens.FlatDisabledOutlineColor.toColor()
+            .copy(alpha = AssistChipTokens.FlatDisabledOutlineOpacity),
+        borderWidth: Dp = AssistChipTokens.FlatOutlineWidth,
+    ): ChipBorder {
+        return remember(
+            borderColor,
+            disabledBorderColor,
+            borderWidth
+        ) {
+            DefaultChipBorder(
+                borderColor = borderColor,
+                disabledBorderColor = disabledBorderColor,
+                borderWidth = borderWidth
+            )
+        }
+    }
+
+    /**
+     * Creates a [ChipColors] that represents the default container, label, and icon colors used in
+     * an elevated [AssistChip].
+     *
+     * @param containerColor the container color of this chip when enabled
+     * @param labelColor the label color of this chip when enabled
+     * @param leadingIconContentColor the color of this chip's start icon when enabled
+     * @param trailingIconContentColor the color of this chip's end icon when enabled
+     * @param disabledContainerColor the container  color of this chip when not enabled
+     * @param disabledLabelColor the label color of this chip when not enabled
+     * @param disabledLeadingIconContentColor the color of this chip's start icon when not enabled
+     * @param disabledTrailingIconContentColor the color of this chip's end icon when not enabled
+     */
+    @Composable
+    fun elevatedAssistChipColors(
+        containerColor: Color = AssistChipTokens.ElevatedContainerColor.toColor(),
+        labelColor: Color = AssistChipTokens.LabelTextColor.toColor(),
+        leadingIconContentColor: Color = AssistChipTokens.IconColor.toColor(),
+        trailingIconContentColor: Color = leadingIconContentColor,
+        disabledContainerColor: Color = AssistChipTokens.ElevatedDisabledContainerColor.toColor()
+            .copy(alpha = AssistChipTokens.ElevatedDisabledContainerOpacity),
+        disabledLabelColor: Color = AssistChipTokens.DisabledLabelTextColor.toColor()
+            .copy(alpha = AssistChipTokens.DisabledLabelTextOpacity),
+        disabledLeadingIconContentColor: Color =
+            AssistChipTokens.DisabledIconColor.toColor()
+                .copy(alpha = AssistChipTokens.DisabledIconOpacity),
+        disabledTrailingIconContentColor: Color = disabledLeadingIconContentColor,
+    ): ChipColors = DefaultChipColors(
+        containerColor = containerColor,
+        labelColor = labelColor,
+        leadingIconContentColor = leadingIconContentColor,
+        trailingIconContentColor = trailingIconContentColor,
+        disabledContainerColor = disabledContainerColor,
+        disabledLabelColor = disabledLabelColor,
+        disabledLeadingIconContentColor = disabledLeadingIconContentColor,
+        disabledTrailingIconContentColor = disabledTrailingIconContentColor
+    )
+
+    /**
+     * Creates a [ChipElevation] that will animate between the provided values according to the
+     * Material specification for an elevated [AssistChip].
+     *
+     * @param defaultElevation the elevation used when the [AssistChip] is has no other
+     * [Interaction]s
+     * @param pressedElevation the elevation used when the chip is pressed.
+     * @param focusedElevation the elevation used when the chip is focused
+     * @param hoveredElevation the elevation used when the chip is hovered
+     * @param draggedElevation the elevation used when the chip is dragged
+     * @param disabledElevation the elevation used when the chip is not enabled
+     */
+    @Composable
+    fun elevatedAssistChipElevation(
+        defaultElevation: Dp = AssistChipTokens.ElevatedContainerElevation,
+        pressedElevation: Dp = AssistChipTokens.ElevatedPressedContainerElevation,
+        focusedElevation: Dp = AssistChipTokens.ElevatedFocusContainerElevation,
+        hoveredElevation: Dp = AssistChipTokens.ElevatedHoverContainerElevation,
+        draggedElevation: Dp = AssistChipTokens.DraggedContainerElevation,
+        disabledElevation: Dp = AssistChipTokens.ElevatedDisabledContainerElevation
+    ): ChipElevation {
+        return remember(
+            defaultElevation,
+            pressedElevation,
+            focusedElevation,
+            hoveredElevation,
+            draggedElevation,
+            disabledElevation
+        ) {
+            DefaultChipElevation(
+                defaultElevation = defaultElevation,
+                pressedElevation = pressedElevation,
+                focusedElevation = focusedElevation,
+                hoveredElevation = hoveredElevation,
+                draggedElevation = draggedElevation,
+                disabledElevation = disabledElevation
+            )
+        }
+    }
+}
+
+/**
+ * Contains the baseline values used by [FilterChip].
+ */
+@ExperimentalMaterial3Api
+object FilterChipDefaults {
+
+    /**
+     * The height applied for a filter chip.
+     * Note that you can override it by applying Modifier.height directly on a chip.
+     */
+    val Height = FilterChipTokens.ContainerHeight
+
+    /**
+     * The size of an filter chip leading icon.
+     */
+    val IconSize = FilterChipTokens.IconSize
+
+    /**
+     * Creates a [SelectableChipColors] that represents the default container and content colors
+     * used in a flat [FilterChip].
+     *
+     * @param containerColor the container color of this chip when enabled
+     * @param labelColor the label color of this chip when enabled
+     * @param iconColor the color of this chip's start and end icons when enabled
+     * @param disabledContainerColor the container color of this chip when not enabled
+     * @param disabledLabelColor the label color of this chip when not enabled
+     * @param disabledLeadingIconColor the color of this chip's start icon when not enabled
+     * @param disabledTrailingIconColor the color of this chip's end icon when not enabled
+     * @param selectedContainerColor the container color of this chip when selected
+     * @param selectedLabelColor the label color of this chip when selected
+     * @param selectedLeadingIconColor the color of this chip's start icon when selected
+     * @param selectedTrailingIconColor the color of this chip's end icon when selected
+     */
+    @Composable
+    fun filterChipColors(
+        containerColor: Color = Color.Transparent,
+        labelColor: Color = FilterChipTokens.UnselectedLabelTextColor.toColor(),
+        iconColor: Color = FilterChipTokens.UnselectedIconColor.toColor(),
+        disabledContainerColor: Color = Color.Transparent,
+        disabledLabelColor: Color = FilterChipTokens.DisabledLabelTextColor.toColor()
+            .copy(alpha = FilterChipTokens.DisabledLabelTextOpacity),
+        disabledLeadingIconColor: Color = FilterChipTokens.DisabledIconColor.toColor()
+            .copy(alpha = FilterChipTokens.DisabledIconOpacity),
+        disabledTrailingIconColor: Color = disabledLeadingIconColor,
+        selectedContainerColor: Color = FilterChipTokens.FlatSelectedContainerColor.toColor(),
+        selectedLabelColor: Color = FilterChipTokens.SelectedLabelTextColor.toColor(),
+        selectedLeadingIconColor: Color = FilterChipTokens.SelectedIconColor.toColor(),
+        selectedTrailingIconColor: Color = selectedLeadingIconColor
+    ): SelectableChipColors = DefaultSelectableChipColors(
+        containerColor = containerColor,
+        labelColor = labelColor,
+        leadingIconColor = iconColor,
+        trailingIconColor = iconColor,
+        disabledContainerColor = disabledContainerColor,
+        disabledLabelColor = disabledLabelColor,
+        disabledLeadingIconColor = disabledLeadingIconColor,
+        disabledTrailingIconColor = disabledTrailingIconColor,
+        selectedContainerColor = selectedContainerColor,
+        selectedLabelColor = selectedLabelColor,
+        selectedLeadingIconColor = selectedLeadingIconColor,
+        selectedTrailingIconColor = selectedTrailingIconColor
+    )
+
+    /**
+     * Creates a [ChipElevation] that will animate between the provided values according to the
+     * Material specification for a flat [FilterChip].
+     *
+     * @param defaultElevation the elevation used when the [FilterChip] is has no other
+     * [Interaction]s
+     * @param pressedElevation the elevation used when the chip is pressed
+     * @param focusedElevation the elevation used when the chip is focused
+     * @param hoveredElevation the elevation used when the chip is hovered
+     * @param draggedElevation the elevation used when the chip is dragged
+     * @param disabledElevation the elevation used when the chip is not enabled
+     */
+    @Composable
+    fun filterChipElevation(
+        defaultElevation: Dp = FilterChipTokens.FlatContainerElevation,
+        pressedElevation: Dp = FilterChipTokens.FlatSelectedPressedContainerElevation,
+        focusedElevation: Dp = FilterChipTokens.FlatSelectedFocusContainerElevation,
+        hoveredElevation: Dp = FilterChipTokens.FlatSelectedHoverContainerElevation,
+        draggedElevation: Dp = FilterChipTokens.DraggedContainerElevation,
+        disabledElevation: Dp = defaultElevation
+    ): SelectableChipElevation {
+        return remember(
+            defaultElevation,
+            pressedElevation,
+            focusedElevation,
+            hoveredElevation,
+            draggedElevation,
+            disabledElevation
+        ) {
+            DefaultSelectableChipElevation(
+                defaultElevation = defaultElevation,
+                pressedElevation = pressedElevation,
+                focusedElevation = focusedElevation,
+                hoveredElevation = hoveredElevation,
+                draggedElevation = draggedElevation,
+                disabledElevation = disabledElevation
+            )
+        }
+    }
+
+    /**
+     * Creates a [ChipBorder] that represents the default border used in a flat [FilterChip].
+     *
+     * @param borderColor the border color of this chip when enabled and not selected
+     * @param selectedBorderColor the border color of this chip when enabled and selected
+     * @param disabledBorderColor the border color of this chip when not enabled and not
+     * selected
+     * @param disabledSelectedBorderColor the border color of this chip when not enabled
+     * but selected
+     * @param borderWidth the border stroke width of this chip when not selected
+     * @param selectedBorderWidth the border stroke width of this chip when selected
+     */
+    @Composable
+    fun filterChipBorder(
+        borderColor: Color = FilterChipTokens.FlatUnselectedOutlineColor.toColor(),
+        selectedBorderColor: Color = Color.Transparent,
+        disabledBorderColor: Color = FilterChipTokens.FlatDisabledUnselectedOutlineColor.toColor()
+            .copy(alpha = FilterChipTokens.FlatDisabledUnselectedOutlineOpacity),
+        disabledSelectedBorderColor: Color = Color.Transparent,
+        borderWidth: Dp = FilterChipTokens.FlatUnselectedOutlineWidth,
+        selectedBorderWidth: Dp = FilterChipTokens.FlatSelectedOutlineWidth,
+    ): SelectableChipBorder {
+        return remember(
+            borderColor,
+            selectedBorderColor,
+            disabledBorderColor,
+            disabledSelectedBorderColor,
+            borderWidth,
+            selectedBorderWidth
+        ) {
+            DefaultSelectableChipBorder(
+                borderColor = borderColor,
+                selectedBorderColor = selectedBorderColor,
+                disabledBorderColor = disabledBorderColor,
+                disabledSelectedBorderColor = disabledSelectedBorderColor,
+                borderWidth = borderWidth,
+                selectedBorderWidth = selectedBorderWidth
+            )
+        }
+    }
+
+    /**
+     * Creates a [SelectableChipColors] that represents the default container and content colors
+     * used in a elevated [FilterChip].
+     *
+     * @param containerColor the container color of this chip when enabled
+     * @param labelColor the label color of this chip when enabled
+     * @param iconColor the color of this chip's start and end icons when enabled
+     * @param disabledContainerColor the container color of this chip when not enabled
+     * @param disabledLabelColor the label color of this chip when not enabled
+     * @param disabledLeadingIconColor the color of this chip's start icon when not enabled
+     * @param disabledTrailingIconColor the color of this chip's end icon when not enabled
+     * @param selectedContainerColor the container  color of this chip when selected
+     * @param selectedLabelColor the label color of this chip when selected
+     * @param selectedLeadingIconColor the color of this chip's start icon when selected
+     * @param selectedTrailingIconColor the color of this chip's end icon when selected
+     */
+    @Composable
+    fun elevatedFilterChipColors(
+        containerColor: Color = FilterChipTokens.ElevatedUnselectedContainerColor.toColor(),
+        labelColor: Color = FilterChipTokens.UnselectedLabelTextColor.toColor(),
+        iconColor: Color = FilterChipTokens.UnselectedIconColor.toColor(),
+        disabledContainerColor: Color = FilterChipTokens.ElevatedDisabledContainerColor.toColor()
+            .copy(alpha = FilterChipTokens.ElevatedDisabledContainerOpacity),
+        disabledLabelColor: Color = FilterChipTokens.DisabledLabelTextColor.toColor()
+            .copy(alpha = FilterChipTokens.DisabledLabelTextOpacity),
+        disabledLeadingIconColor: Color = FilterChipTokens.DisabledIconColor.toColor()
+            .copy(alpha = FilterChipTokens.DisabledIconOpacity),
+        disabledTrailingIconColor: Color = disabledLeadingIconColor,
+        selectedContainerColor: Color = FilterChipTokens.ElevatedSelectedContainerColor.toColor(),
+        selectedLabelColor: Color = FilterChipTokens.SelectedLabelTextColor.toColor(),
+        selectedLeadingIconColor: Color = FilterChipTokens.SelectedIconColor.toColor(),
+        selectedTrailingIconColor: Color = selectedLeadingIconColor
+    ): SelectableChipColors = DefaultSelectableChipColors(
+        containerColor = containerColor,
+        labelColor = labelColor,
+        leadingIconColor = iconColor,
+        trailingIconColor = iconColor,
+        disabledContainerColor = disabledContainerColor,
+        disabledLabelColor = disabledLabelColor,
+        disabledLeadingIconColor = disabledLeadingIconColor,
+        disabledTrailingIconColor = disabledTrailingIconColor,
+        selectedContainerColor = selectedContainerColor,
+        selectedLabelColor = selectedLabelColor,
+        selectedLeadingIconColor = selectedLeadingIconColor,
+        selectedTrailingIconColor = selectedTrailingIconColor
+    )
+
+    /**
+     * Creates a [ChipElevation] that will animate between the provided values according to the
+     * Material specification for an elevated [FilterChip].
+     *
+     * @param defaultElevation the elevation used when the chip is has no other
+     * [Interaction]s
+     * @param pressedElevation the elevation used when the chip is pressed
+     * @param focusedElevation the elevation used when the chip is focused
+     * @param hoveredElevation the elevation used when the chip is hovered
+     * @param draggedElevation the elevation used when the chip is dragged
+     * @param disabledElevation the elevation used when the chip is not enabled
+     */
+    @Composable
+    fun elevatedFilterChipElevation(
+        defaultElevation: Dp = FilterChipTokens.ElevatedContainerElevation,
+        pressedElevation: Dp = FilterChipTokens.ElevatedPressedContainerElevation,
+        focusedElevation: Dp = FilterChipTokens.ElevatedFocusContainerElevation,
+        hoveredElevation: Dp = FilterChipTokens.ElevatedHoverContainerElevation,
+        draggedElevation: Dp = FilterChipTokens.DraggedContainerElevation,
+        disabledElevation: Dp = FilterChipTokens.ElevatedDisabledContainerElevation
+    ): SelectableChipElevation {
+        return remember(
+            defaultElevation,
+            pressedElevation,
+            focusedElevation,
+            hoveredElevation,
+            draggedElevation,
+            disabledElevation
+        ) {
+            DefaultSelectableChipElevation(
+                defaultElevation = defaultElevation,
+                pressedElevation = pressedElevation,
+                focusedElevation = focusedElevation,
+                hoveredElevation = hoveredElevation,
+                draggedElevation = draggedElevation,
+                disabledElevation = disabledElevation
+            )
+        }
+    }
+}
+
+/**
+ * Contains the baseline values used by an [InputChip].
+ */
+@ExperimentalMaterial3Api
+object InputChipDefaults {
+    /**
+     * The height applied for a input chip.
+     * Note that you can override it by applying Modifier.height directly on a chip.
+     */
+    val Height = InputChipTokens.ContainerHeight
+
+    /**
+     * The size of an input chip icon.
+     */
+    val IconSize = InputChipTokens.LeadingIconSize
+
+    /**
+     * The size of an input chip avatar.
+     */
+    val AvatarSize = InputChipTokens.AvatarSize
+
+    /**
+     * Creates a [ChipColors] that represents the default container, label, and icon colors used in
+     * an [InputChip].
+     *
+     * @param containerColor the container color of this chip when enabled
+     * @param labelColor the label color of this chip when enabled
+     * @param leadingIconContentColor the color of this chip's start icon when enabled
+     * @param trailingIconContentColor the color of this chip's end icon when enabled
+     * @param disabledContainerColor the container color of this chip when not enabled
+     * @param disabledLabelColor the label color of this chip when not enabled
+     * @param disabledLeadingIconContentColor the color of this chip's start icon when not enabled
+     * @param disabledTrailingIconContentColor the color of this chip's end icon when not enabled
+     */
+    @Composable
+    fun inputChipColors(
+        containerColor: Color = Color.Transparent,
+        labelColor: Color = InputChipTokens.LabelTextColor.toColor(),
+        leadingIconContentColor: Color = InputChipTokens.LeadingIconColor.toColor(),
+        trailingIconContentColor: Color = InputChipTokens.TrailingIconColor.toColor(),
+        disabledContainerColor: Color = Color.Transparent,
+        disabledLabelColor: Color = InputChipTokens.DisabledLabelTextColor.toColor()
+            .copy(alpha = InputChipTokens.DisabledLabelTextOpacity),
+        disabledLeadingIconContentColor: Color =
+            InputChipTokens.DisabledLeadingIconColor.toColor()
+                .copy(alpha = InputChipTokens.DisabledLeadingIconOpacity),
+        disabledTrailingIconContentColor: Color =
+            InputChipTokens.DisabledTrailingIconColor.toColor()
+                .copy(alpha = InputChipTokens.DisabledTrailingIconOpacity),
+    ): ChipColors = DefaultChipColors(
+        containerColor = containerColor,
+        labelColor = labelColor,
+        leadingIconContentColor = leadingIconContentColor,
+        trailingIconContentColor = trailingIconContentColor,
+        disabledContainerColor = disabledContainerColor,
+        disabledLabelColor = disabledLabelColor,
+        disabledLeadingIconContentColor = disabledLeadingIconContentColor,
+        disabledTrailingIconContentColor = disabledTrailingIconContentColor
+    )
+
+    /**
+     * Creates a [ChipElevation] that will animate between the provided values according to the
+     * Material specification for an [InputChip].
+     *
+     * @param defaultElevation the elevation used when the [InputChip] is has no other
+     * [Interaction]s
+     * @param pressedElevation the elevation used when the chip is pressed
+     * @param focusedElevation the elevation used when the chip is focused
+     * @param hoveredElevation the elevation used when the chip is hovered
+     * @param draggedElevation the elevation used when the chip is dragged
+     * @param disabledElevation the elevation used when the chip is not enabled
+     */
+    @Composable
+    fun inputChipElevation(
+        defaultElevation: Dp = InputChipTokens.ContainerElevation,
+        pressedElevation: Dp = defaultElevation,
+        focusedElevation: Dp = defaultElevation,
+        hoveredElevation: Dp = defaultElevation,
+        draggedElevation: Dp = InputChipTokens.DraggedContainerElevation,
+        disabledElevation: Dp = defaultElevation
+    ): ChipElevation {
+        return remember(
+            defaultElevation,
+            pressedElevation,
+            focusedElevation,
+            hoveredElevation,
+            draggedElevation,
+            disabledElevation
+        ) {
+            DefaultChipElevation(
+                defaultElevation = defaultElevation,
+                pressedElevation = pressedElevation,
+                focusedElevation = focusedElevation,
+                hoveredElevation = hoveredElevation,
+                draggedElevation = draggedElevation,
+                disabledElevation = disabledElevation
+            )
+        }
+    }
+
+    /**
+     * Creates a [ChipBorder] that represents the default border used in an [InputChip].
+     *
+     * @param borderColor the border color of this chip when enabled
+     * @param disabledBorderColor the border color of this chip when not enabled
+     * @param borderWidth the border stroke width of this chip
+     */
+    @Composable
+    fun inputChipBorder(
+        borderColor: Color = InputChipTokens.OutlineColor.toColor(),
+        disabledBorderColor: Color = InputChipTokens.DisabledOutlineColor.toColor()
+            .copy(alpha = InputChipTokens.DisabledOutlineOpacity),
+        borderWidth: Dp = InputChipTokens.OutlineWidth,
+    ): ChipBorder {
+        return remember(
+            borderColor,
+            disabledBorderColor,
+            borderWidth
+        ) {
+            DefaultChipBorder(
+                borderColor = borderColor,
+                disabledBorderColor = disabledBorderColor,
+                borderWidth = borderWidth
+            )
+        }
+    }
+}
+
+/**
+ * Contains the baseline values used by [SuggestionChip].
+ */
+@ExperimentalMaterial3Api
+object SuggestionChipDefaults {
+    /**
+     * The height applied for a suggestion chip.
+     * Note that you can override it by applying Modifier.height directly on a chip.
+     */
+    val Height = SuggestionChipTokens.ContainerHeight
+
+    /**
+     * The size of an suggestion chip icon.
+     */
+    // TODO(b/229778210): Read from the tokens when available.
+    val IconSize = 18.dp
+
+    /**
+     * Creates a [ChipColors] that represents the default container, label, and icon colors used in
+     * a flat [SuggestionChip].
+     *
+     * @param containerColor the container color of this chip when enabled
+     * @param labelColor the label color of this chip when enabled
+     * @param iconContentColor the color of this chip's icon when enabled
+     * @param disabledContainerColor the container color of this chip when not enabled
+     * @param disabledLabelColor the label color of this chip when not enabled
+     * @param disabledIconContentColor the color of this chip's icon when not enabled
+     */
+    @Composable
+    fun suggestionChipColors(
+        containerColor: Color = Color.Transparent,
+        labelColor: Color = SuggestionChipTokens.LabelTextColor.toColor(),
+        // TODO(b/229778210): Read from the tokens when available
+        //  (i.e. SuggestionChipTokens.IconColor.toColor()).
+        iconContentColor: Color = MaterialTheme.colorScheme.onSurfaceVariant,
+        disabledContainerColor: Color = Color.Transparent,
+        disabledLabelColor: Color = SuggestionChipTokens.DisabledLabelTextColor.toColor()
+            .copy(alpha = SuggestionChipTokens.DisabledLabelTextOpacity),
+        // TODO(b/229778210): Read from the tokens when available.
+        disabledIconContentColor: Color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.38f)
+    ): ChipColors = DefaultChipColors(
+        containerColor = containerColor,
+        labelColor = labelColor,
+        leadingIconContentColor = iconContentColor,
+        trailingIconContentColor = Color.Unspecified,
+        disabledContainerColor = disabledContainerColor,
+        disabledLabelColor = disabledLabelColor,
+        disabledLeadingIconContentColor = disabledIconContentColor,
+        disabledTrailingIconContentColor = Color.Unspecified
+    )
+
+    /**
+     * Creates a [ChipElevation] that will animate between the provided values according to the
+     * Material specification for a flat [SuggestionChip].
+     *
+     * @param defaultElevation the elevation used when the chip is has no other
+     * [Interaction]s
+     * @param pressedElevation the elevation used when the chip is pressed
+     * @param focusedElevation the elevation used when the chip is focused
+     * @param hoveredElevation the elevation used when the chip is hovered
+     * @param draggedElevation the elevation used when the chip is dragged
+     * @param disabledElevation the elevation used when the chip is not enabled
+     */
+    @Composable
+    fun suggestionChipElevation(
+        defaultElevation: Dp = SuggestionChipTokens.FlatContainerElevation,
+        pressedElevation: Dp = defaultElevation,
+        focusedElevation: Dp = defaultElevation,
+        hoveredElevation: Dp = defaultElevation,
+        draggedElevation: Dp = SuggestionChipTokens.DraggedContainerElevation,
+        disabledElevation: Dp = defaultElevation
+    ): ChipElevation {
+        return remember(
+            defaultElevation,
+            pressedElevation,
+            focusedElevation,
+            hoveredElevation,
+            draggedElevation,
+            disabledElevation
+        ) {
+            DefaultChipElevation(
+                defaultElevation = defaultElevation,
+                pressedElevation = pressedElevation,
+                focusedElevation = focusedElevation,
+                hoveredElevation = hoveredElevation,
+                draggedElevation = draggedElevation,
+                disabledElevation = disabledElevation
+            )
+        }
+    }
+
+    /**
+     * Creates a [ChipBorder] that represents the default border used in a flat [SuggestionChip].
+     *
+     * @param borderColor the border color of this chip when enabled
+     * @param disabledBorderColor the border color of this chip when not enabled
+     * @param borderWidth the border stroke width of this chip
+     */
+    @Composable
+    fun suggestionChipBorder(
+        borderColor: Color = SuggestionChipTokens.FlatOutlineColor.toColor(),
+        disabledBorderColor: Color = SuggestionChipTokens.FlatDisabledOutlineColor.toColor()
+            .copy(alpha = SuggestionChipTokens.FlatDisabledOutlineOpacity),
+        borderWidth: Dp = SuggestionChipTokens.FlatOutlineWidth,
+    ): ChipBorder {
+        return remember(
+            borderColor, disabledBorderColor, borderWidth
+        ) {
+            DefaultChipBorder(
+                borderColor = borderColor,
+                disabledBorderColor = disabledBorderColor,
+                borderWidth = borderWidth
+            )
+        }
+    }
+
+    /**
+     * Creates a [ChipColors] that represents the default container, label, and icon colors used in
+     * an elevated [SuggestionChip].
+     *
+     * @param containerColor the container color of this chip when enabled
+     * @param labelColor the label color of this chip when enabled
+     * @param iconContentColor the color of this chip's icon when enabled
+     * @param disabledContainerColor the container color of this chip when not enabled
+     * @param disabledLabelColor the label color of this chip when not enabled
+     * @param disabledIconContentColor the color of this chip's icon when not enabled
+     */
+    @Composable
+    fun elevatedSuggestionChipColors(
+        containerColor: Color = SuggestionChipTokens.ElevatedContainerColor.toColor(),
+        labelColor: Color = SuggestionChipTokens.LabelTextColor.toColor(),
+        // TODO(b/229778210) Read from the tokens when available
+        //  (i.e. SuggestionChipTokens.IconColor.toColor()).
+        iconContentColor: Color = MaterialTheme.colorScheme.onSurfaceVariant,
+        disabledContainerColor: Color =
+            SuggestionChipTokens.ElevatedDisabledContainerColor.toColor()
+                .copy(alpha = SuggestionChipTokens.ElevatedDisabledContainerOpacity),
+        disabledLabelColor: Color = SuggestionChipTokens.DisabledLabelTextColor.toColor()
+            .copy(alpha = SuggestionChipTokens.DisabledLabelTextOpacity),
+        // TODO(b/229778210): Read from the tokens when available.
+        disabledIconContentColor: Color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.38f)
+    ): ChipColors = DefaultChipColors(
+        containerColor = containerColor,
+        labelColor = labelColor,
+        leadingIconContentColor = iconContentColor,
+        trailingIconContentColor = Color.Unspecified,
+        disabledContainerColor = disabledContainerColor,
+        disabledLabelColor = disabledLabelColor,
+        disabledLeadingIconContentColor = disabledIconContentColor,
+        disabledTrailingIconContentColor = Color.Unspecified
+    )
+
+    /**
+     * Creates a [ChipElevation] that will animate between the provided values according to the
+     * Material specification for an elevated [SuggestionChip].
+     *
+     * @param defaultElevation the elevation used when the chip is has no other
+     * [Interaction]s
+     * @param pressedElevation the elevation used when the chip is pressed
+     * @param focusedElevation the elevation used when the chip is focused
+     * @param hoveredElevation the elevation used when the chip is hovered
+     * @param draggedElevation the elevation used when the chip is dragged
+     * @param disabledElevation the elevation used when the chip is not enabled
+     */
+    @Composable
+    fun elevatedSuggestionChipElevation(
+        defaultElevation: Dp = SuggestionChipTokens.ElevatedContainerElevation,
+        pressedElevation: Dp = SuggestionChipTokens.ElevatedPressedContainerElevation,
+        focusedElevation: Dp = SuggestionChipTokens.ElevatedFocusContainerElevation,
+        hoveredElevation: Dp = SuggestionChipTokens.ElevatedHoverContainerElevation,
+        draggedElevation: Dp = SuggestionChipTokens.DraggedContainerElevation,
+        disabledElevation: Dp = SuggestionChipTokens.ElevatedDisabledContainerElevation
+    ): ChipElevation {
+        return remember(
+            defaultElevation,
+            pressedElevation,
+            focusedElevation,
+            hoveredElevation,
+            draggedElevation,
+            disabledElevation
+        ) {
+            DefaultChipElevation(
+                defaultElevation = defaultElevation,
+                pressedElevation = pressedElevation,
+                focusedElevation = focusedElevation,
+                hoveredElevation = hoveredElevation,
+                draggedElevation = draggedElevation,
+                disabledElevation = disabledElevation
+            )
+        }
+    }
+}
+
+@ExperimentalMaterial3Api
+@Composable
+private fun Chip(
+    modifier: Modifier,
+    onClick: () -> Unit,
+    enabled: Boolean,
+    label: @Composable RowScope.() -> Unit,
+    labelTextStyle: TextStyle,
+    labelColor: Color,
+    leadingIcon: @Composable (() -> Unit)?,
+    avatar: @Composable (() -> Unit)?,
+    trailingIcon: @Composable (() -> Unit)?,
+    leadingIconColor: Color,
+    trailingIconColor: Color,
+    containerColor: Color,
+    tonalElevation: Dp,
+    shadowElevation: Dp,
+    minHeight: Dp,
+    paddingValues: PaddingValues,
+    shape: Shape,
+    border: BorderStroke?,
+    interactionSource: MutableInteractionSource,
+) {
+    Surface(
+        onClick = onClick,
+        modifier = modifier,
+        enabled = enabled,
+        shape = shape,
+        color = containerColor,
+        tonalElevation = tonalElevation,
+        shadowElevation = shadowElevation,
+        border = border,
+        interactionSource = interactionSource,
+    ) {
+        ChipContent(
+            label = label,
+            labelTextStyle = labelTextStyle,
+            labelColor = labelColor,
+            leadingIcon = leadingIcon,
+            avatar = avatar,
+            trailingIcon = trailingIcon,
+            leadingIconColor = leadingIconColor,
+            trailingIconColor = trailingIconColor,
+            minHeight = minHeight,
+            paddingValues = paddingValues
+        )
+    }
+}
+
+@ExperimentalMaterial3Api
+@Composable
+private fun SelectableChip(
+    selected: Boolean,
+    modifier: Modifier,
+    onClick: () -> Unit,
+    enabled: Boolean,
+    label: @Composable RowScope.() -> Unit,
+    labelTextStyle: TextStyle,
+    leadingIcon: @Composable (() -> Unit)?,
+    trailingIcon: @Composable (() -> Unit)?,
+    elevation: SelectableChipElevation?,
+    colors: SelectableChipColors,
+    minHeight: Dp,
+    paddingValues: PaddingValues,
+    shape: Shape,
+    border: BorderStroke?,
+    interactionSource: MutableInteractionSource
+) {
+    // TODO(b/229794614): Animate transition between unselected and selected.
+    Surface(
+        selected = selected,
+        onClick = onClick,
+        modifier = modifier.semantics { role = Role.Checkbox },
+        enabled = enabled,
+        shape = shape,
+        color = colors.containerColor(enabled, selected).value,
+        tonalElevation = elevation?.tonalElevation(enabled, selected, interactionSource)?.value
+            ?: 0.dp,
+        shadowElevation = elevation?.shadowElevation(enabled, selected, interactionSource)?.value
+            ?: 0.dp,
+        border = border,
+        interactionSource = interactionSource,
+    ) {
+        ChipContent(
+            label = label,
+            labelTextStyle = labelTextStyle,
+            leadingIcon = leadingIcon,
+            avatar = null,
+            labelColor = colors.labelColor(enabled, selected).value,
+            trailingIcon = trailingIcon,
+            leadingIconColor = colors.leadingIconContentColor(enabled, selected).value,
+            trailingIconColor = colors.trailingIconContentColor(enabled, selected).value,
+            minHeight = minHeight,
+            paddingValues = paddingValues
+        )
+    }
+}
+
+@Composable
+private fun ChipContent(
+    label: @Composable (RowScope.() -> Unit),
+    labelTextStyle: TextStyle,
+    labelColor: Color,
+    leadingIcon: @Composable (() -> Unit)?,
+    avatar: @Composable (() -> Unit)?,
+    trailingIcon: @Composable (() -> Unit)?,
+    leadingIconColor: Color,
+    trailingIconColor: Color,
+    minHeight: Dp,
+    paddingValues: PaddingValues
+) {
+    CompositionLocalProvider(
+        LocalContentColor provides labelColor,
+        LocalTextStyle provides labelTextStyle
+    ) {
+        Row(
+            Modifier.defaultMinSize(minHeight = minHeight).padding(paddingValues),
+            horizontalArrangement = Arrangement.Start,
+            verticalAlignment = Alignment.CenterVertically
+        ) {
+            if (avatar != null) {
+                avatar()
+            } else if (leadingIcon != null) {
+                CompositionLocalProvider(
+                    LocalContentColor provides leadingIconColor, content = leadingIcon
+                )
+            }
+            Spacer(Modifier.width(HorizontalElementsPadding))
+            label()
+            Spacer(Modifier.width(HorizontalElementsPadding))
+            if (trailingIcon != null) {
+                CompositionLocalProvider(
+                    LocalContentColor provides trailingIconColor, content = trailingIcon
+                )
+            }
+        }
+    }
+}
+
+/** Default [ChipElevation] implementation. */
+@ExperimentalMaterial3Api
+@Immutable
+private class DefaultChipElevation(
+    private val defaultElevation: Dp,
+    private val pressedElevation: Dp,
+    private val focusedElevation: Dp,
+    private val hoveredElevation: Dp,
+    private val draggedElevation: Dp,
+    private val disabledElevation: Dp
+) : ChipElevation {
+    @Composable
+    override fun tonalElevation(
+        enabled: Boolean,
+        interactionSource: InteractionSource
+    ): State<Dp> {
+        return animateElevation(enabled = enabled, interactionSource = interactionSource)
+    }
+
+    @Composable
+    override fun shadowElevation(
+        enabled: Boolean,
+        interactionSource: InteractionSource
+    ): State<Dp> {
+        return animateElevation(enabled = enabled, interactionSource = interactionSource)
+    }
+
+    @Composable
+    private fun animateElevation(
+        enabled: Boolean,
+        interactionSource: InteractionSource
+    ): State<Dp> {
+        val interactions = remember { mutableStateListOf<Interaction>() }
+        LaunchedEffect(interactionSource) {
+            interactionSource.interactions.collect { interaction ->
+                when (interaction) {
+                    is HoverInteraction.Enter -> {
+                        interactions.add(interaction)
+                    }
+                    is HoverInteraction.Exit -> {
+                        interactions.remove(interaction.enter)
+                    }
+                    is FocusInteraction.Focus -> {
+                        interactions.add(interaction)
+                    }
+                    is FocusInteraction.Unfocus -> {
+                        interactions.remove(interaction.focus)
+                    }
+                    is PressInteraction.Press -> {
+                        interactions.add(interaction)
+                    }
+                    is PressInteraction.Release -> {
+                        interactions.remove(interaction.press)
+                    }
+                    is PressInteraction.Cancel -> {
+                        interactions.remove(interaction.press)
+                    }
+                    is DragInteraction.Start -> {
+                        interactions.add(interaction)
+                    }
+                    is DragInteraction.Stop -> {
+                        interactions.remove(interaction.start)
+                    }
+                    is DragInteraction.Cancel -> {
+                        interactions.remove(interaction.start)
+                    }
+                }
+            }
+        }
+
+        val interaction = interactions.lastOrNull()
+
+        val target = if (!enabled) {
+            disabledElevation
+        } else {
+            when (interaction) {
+                is PressInteraction.Press -> pressedElevation
+                is HoverInteraction.Enter -> hoveredElevation
+                is FocusInteraction.Focus -> focusedElevation
+                is DragInteraction.Start -> draggedElevation
+                else -> defaultElevation
+            }
+        }
+
+        val animatable = remember { Animatable(target, Dp.VectorConverter) }
+
+        if (!enabled) {
+            // No transition when moving to a disabled state
+            LaunchedEffect(target) { animatable.snapTo(target) }
+        } else {
+            LaunchedEffect(target) {
+                val lastInteraction = when (animatable.targetValue) {
+                    pressedElevation -> PressInteraction.Press(Offset.Zero)
+                    hoveredElevation -> HoverInteraction.Enter()
+                    focusedElevation -> FocusInteraction.Focus()
+                    draggedElevation -> DragInteraction.Start()
+                    else -> null
+                }
+                animatable.animateElevation(
+                    from = lastInteraction, to = interaction, target = target
+                )
+            }
+        }
+
+        return animatable.asState()
+    }
+}
+
+/**
+ * Default [SelectableChipElevation] implementation.
+ *
+ * Note that this default implementation does not take into consideration the `selectable` state
+ * passed into its [tonalElevation] and [shadowElevation]. If you wish to apply that state, use a
+ * different [SelectableChipElevation].
+ */
+@ExperimentalMaterial3Api
+@Immutable
+private class DefaultSelectableChipElevation(
+    private val defaultElevation: Dp,
+    private val pressedElevation: Dp,
+    private val focusedElevation: Dp,
+    private val hoveredElevation: Dp,
+    private val draggedElevation: Dp,
+    private val disabledElevation: Dp
+) : SelectableChipElevation {
+    @Composable
+    override fun tonalElevation(
+        enabled: Boolean,
+        selected: Boolean,
+        interactionSource: InteractionSource
+    ): State<Dp> {
+        return animateElevation(enabled = enabled, interactionSource = interactionSource)
+    }
+
+    @Composable
+    override fun shadowElevation(
+        enabled: Boolean,
+        selected: Boolean,
+        interactionSource: InteractionSource
+    ): State<Dp> {
+        return animateElevation(enabled = enabled, interactionSource = interactionSource)
+    }
+
+    @Composable
+    private fun animateElevation(
+        enabled: Boolean,
+        interactionSource: InteractionSource
+    ): State<Dp> {
+        val interactions = remember { mutableStateListOf<Interaction>() }
+        LaunchedEffect(interactionSource) {
+            interactionSource.interactions.collect { interaction ->
+                when (interaction) {
+                    is HoverInteraction.Enter -> {
+                        interactions.add(interaction)
+                    }
+                    is HoverInteraction.Exit -> {
+                        interactions.remove(interaction.enter)
+                    }
+                    is FocusInteraction.Focus -> {
+                        interactions.add(interaction)
+                    }
+                    is FocusInteraction.Unfocus -> {
+                        interactions.remove(interaction.focus)
+                    }
+                    is PressInteraction.Press -> {
+                        interactions.add(interaction)
+                    }
+                    is PressInteraction.Release -> {
+                        interactions.remove(interaction.press)
+                    }
+                    is PressInteraction.Cancel -> {
+                        interactions.remove(interaction.press)
+                    }
+                    is DragInteraction.Start -> {
+                        interactions.add(interaction)
+                    }
+                    is DragInteraction.Stop -> {
+                        interactions.remove(interaction.start)
+                    }
+                    is DragInteraction.Cancel -> {
+                        interactions.remove(interaction.start)
+                    }
+                }
+            }
+        }
+
+        val interaction = interactions.lastOrNull()
+
+        val target = if (!enabled) {
+            disabledElevation
+        } else {
+            when (interaction) {
+                is PressInteraction.Press -> pressedElevation
+                is HoverInteraction.Enter -> hoveredElevation
+                is FocusInteraction.Focus -> focusedElevation
+                is DragInteraction.Start -> draggedElevation
+                else -> defaultElevation
+            }
+        }
+
+        val animatable = remember { Animatable(target, Dp.VectorConverter) }
+
+        if (!enabled) {
+            // No transition when moving to a disabled state
+            LaunchedEffect(target) { animatable.snapTo(target) }
+        } else {
+            LaunchedEffect(target) {
+                val lastInteraction = when (animatable.targetValue) {
+                    pressedElevation -> PressInteraction.Press(Offset.Zero)
+                    hoveredElevation -> HoverInteraction.Enter()
+                    focusedElevation -> FocusInteraction.Focus()
+                    draggedElevation -> DragInteraction.Start()
+                    else -> null
+                }
+                animatable.animateElevation(
+                    from = lastInteraction, to = interaction, target = target
+                )
+            }
+        }
+
+        return animatable.asState()
+    }
+}
+
+/**
+ * Default [ChipColors] implementation.
+ */
+@ExperimentalMaterial3Api
+@Immutable
+private class DefaultChipColors(
+    private val containerColor: Color,
+    private val labelColor: Color,
+    private val leadingIconContentColor: Color,
+    private val trailingIconContentColor: Color,
+    private val disabledContainerColor: Color,
+    private val disabledLabelColor: Color,
+    private val disabledLeadingIconContentColor: Color,
+    private val disabledTrailingIconContentColor: Color
+    // TODO(b/113855296): Support other states: hover, focus, drag
+) : ChipColors {
+    @Composable
+    override fun containerColor(enabled: Boolean): State<Color> {
+        return rememberUpdatedState(if (enabled) containerColor else disabledContainerColor)
+    }
+
+    @Composable
+    override fun labelColor(enabled: Boolean): State<Color> {
+        return rememberUpdatedState(if (enabled) labelColor else disabledLabelColor)
+    }
+
+    @Composable
+    override fun leadingIconContentColor(enabled: Boolean): State<Color> {
+        return rememberUpdatedState(
+            if (enabled) leadingIconContentColor else disabledLeadingIconContentColor
+        )
+    }
+
+    @Composable
+    override fun trailingIconContentColor(enabled: Boolean): State<Color> {
+        return rememberUpdatedState(
+            if (enabled) trailingIconContentColor else disabledTrailingIconContentColor
+        )
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other == null || this::class != other::class) return false
+
+        other as DefaultChipColors
+
+        if (containerColor != other.containerColor) return false
+        if (labelColor != other.labelColor) return false
+        if (leadingIconContentColor != other.leadingIconContentColor) return false
+        if (trailingIconContentColor != other.trailingIconContentColor) return false
+        if (disabledContainerColor != other.disabledContainerColor) return false
+        if (disabledLabelColor != other.disabledLabelColor) return false
+        if (disabledLeadingIconContentColor != other.disabledLeadingIconContentColor) return false
+        if (disabledTrailingIconContentColor != other.disabledTrailingIconContentColor) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = containerColor.hashCode()
+        result = 31 * result + labelColor.hashCode()
+        result = 31 * result + leadingIconContentColor.hashCode()
+        result = 31 * result + trailingIconContentColor.hashCode()
+        result = 31 * result + disabledContainerColor.hashCode()
+        result = 31 * result + disabledLabelColor.hashCode()
+        result = 31 * result + disabledLeadingIconContentColor.hashCode()
+        result = 31 * result + disabledTrailingIconContentColor.hashCode()
+
+        return result
+    }
+}
+
+/**
+ * Default [SelectableChipColors] implementation.
+ */
+@ExperimentalMaterial3Api
+@Immutable
+private class DefaultSelectableChipColors(
+    private val containerColor: Color,
+    private val labelColor: Color,
+    private val leadingIconColor: Color,
+    private val trailingIconColor: Color,
+    private val disabledContainerColor: Color,
+    private val disabledLabelColor: Color,
+    private val disabledLeadingIconColor: Color,
+    private val disabledTrailingIconColor: Color,
+    private val selectedContainerColor: Color,
+    private val selectedLabelColor: Color,
+    private val selectedLeadingIconColor: Color,
+    private val selectedTrailingIconColor: Color
+    // TODO(b/113855296): Support other states: hover, focus, drag
+) : SelectableChipColors {
+    @Composable
+    override fun containerColor(enabled: Boolean, selected: Boolean): State<Color> {
+        val target = when {
+            !enabled -> disabledContainerColor
+            !selected -> containerColor
+            else -> selectedContainerColor
+        }
+        return rememberUpdatedState(target)
+    }
+
+    @Composable
+    override fun labelColor(enabled: Boolean, selected: Boolean): State<Color> {
+        val target = when {
+            !enabled -> disabledLabelColor
+            !selected -> labelColor
+            else -> selectedLabelColor
+        }
+        return rememberUpdatedState(target)
+    }
+
+    @Composable
+    override fun leadingIconContentColor(enabled: Boolean, selected: Boolean): State<Color> {
+        val target = when {
+            !enabled -> disabledLeadingIconColor
+            !selected -> leadingIconColor
+            else -> selectedLeadingIconColor
+        }
+        return rememberUpdatedState(target)
+    }
+
+    @Composable
+    override fun trailingIconContentColor(enabled: Boolean, selected: Boolean): State<Color> {
+        val target = when {
+            !enabled -> disabledTrailingIconColor
+            !selected -> trailingIconColor
+            else -> selectedTrailingIconColor
+        }
+        return rememberUpdatedState(target)
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other == null || this::class != other::class) return false
+
+        other as DefaultSelectableChipColors
+
+        if (containerColor != other.containerColor) return false
+        if (labelColor != other.labelColor) return false
+        if (leadingIconColor != other.leadingIconColor) return false
+        if (trailingIconColor != other.trailingIconColor) return false
+        if (disabledContainerColor != other.disabledContainerColor) return false
+        if (disabledLabelColor != other.disabledLabelColor) return false
+        if (disabledLeadingIconColor != other.disabledLeadingIconColor) return false
+        if (disabledTrailingIconColor != other.disabledTrailingIconColor) return false
+        if (selectedContainerColor != other.selectedContainerColor) return false
+        if (selectedLabelColor != other.selectedLabelColor) return false
+        if (selectedLeadingIconColor != other.selectedLeadingIconColor) return false
+        if (selectedTrailingIconColor != other.selectedTrailingIconColor) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = containerColor.hashCode()
+        result = 31 * result + labelColor.hashCode()
+        result = 31 * result + leadingIconColor.hashCode()
+        result = 31 * result + trailingIconColor.hashCode()
+        result = 31 * result + disabledContainerColor.hashCode()
+        result = 31 * result + disabledLabelColor.hashCode()
+        result = 31 * result + disabledLeadingIconColor.hashCode()
+        result = 31 * result + disabledTrailingIconColor.hashCode()
+        result = 31 * result + selectedContainerColor.hashCode()
+        result = 31 * result + selectedLabelColor.hashCode()
+        result = 31 * result + selectedLeadingIconColor.hashCode()
+        result = 31 * result + selectedTrailingIconColor.hashCode()
+
+        return result
+    }
+}
+
+/**
+ * Default [SelectableChipBorder] implementation.
+ */
+@ExperimentalMaterial3Api
+@Immutable
+private class DefaultSelectableChipBorder(
+    private val borderColor: Color,
+    private val selectedBorderColor: Color,
+    private val disabledBorderColor: Color,
+    private val disabledSelectedBorderColor: Color,
+    private val borderWidth: Dp,
+    private val selectedBorderWidth: Dp
+) : SelectableChipBorder {
+    @Composable
+    override fun borderStroke(enabled: Boolean, selected: Boolean): State<BorderStroke> {
+        val color = if (enabled) {
+            if (selected) selectedBorderColor else borderColor
+        } else {
+            if (selected) disabledSelectedBorderColor else disabledBorderColor
+        }
+        return rememberUpdatedState(
+            BorderStroke(if (selected) selectedBorderWidth else borderWidth, color)
+        )
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other == null || this::class != other::class) return false
+
+        other as DefaultSelectableChipBorder
+
+        if (borderColor != other.borderColor) return false
+        if (selectedBorderColor != other.selectedBorderColor) return false
+        if (disabledBorderColor != other.disabledBorderColor) return false
+        if (disabledSelectedBorderColor != other.disabledSelectedBorderColor) return false
+        if (borderWidth != other.borderWidth) return false
+        if (selectedBorderWidth != other.selectedBorderWidth) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = borderColor.hashCode()
+        result = 31 * result + selectedBorderColor.hashCode()
+        result = 31 * result + disabledBorderColor.hashCode()
+        result = 31 * result + disabledSelectedBorderColor.hashCode()
+        result = 31 * result + borderWidth.hashCode()
+        result = 31 * result + selectedBorderWidth.hashCode()
+
+        return result
+    }
+}
+
+/**
+ * Default [ChipBorder] implementation.
+ */
+@ExperimentalMaterial3Api
+@Immutable
+private class DefaultChipBorder(
+    private val borderColor: Color,
+    private val disabledBorderColor: Color,
+    private val borderWidth: Dp,
+) : ChipBorder {
+    @Composable
+    override fun borderStroke(enabled: Boolean): State<BorderStroke?> {
+        return rememberUpdatedState(
+            BorderStroke(borderWidth, if (enabled) borderColor else disabledBorderColor)
+        )
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other == null || this::class != other::class) return false
+
+        other as DefaultChipBorder
+
+        if (borderColor != other.borderColor) return false
+        if (disabledBorderColor != other.disabledBorderColor) return false
+        if (borderWidth != other.borderWidth) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = borderColor.hashCode()
+        result = 31 * result + disabledBorderColor.hashCode()
+        result = 31 * result + borderWidth.hashCode()
+
+        return result
+    }
+}
+
+/**
+ * Returns the [PaddingValues] for the input chip.
+ */
+private fun inputChipPadding(
+    hasAvatar: Boolean = false,
+    hasLeadingIcon: Boolean = false,
+    hasTrailingIcon: Boolean = false
+): PaddingValues {
+    val start = if (hasAvatar || !hasLeadingIcon) 4.dp else 8.dp
+    val end = if (hasTrailingIcon) 8.dp else 4.dp
+    return PaddingValues(start = start, end = end)
+}
+
+/**
+ * The padding between the elements in the chip.
+ */
+private val HorizontalElementsPadding = 8.dp
+
+/**
+ * Returns the [PaddingValues] for the assist chip.
+ */
+private val AssistChipPadding = PaddingValues(horizontal = HorizontalElementsPadding)
+
+/**
+ * [PaddingValues] for the filter chip.
+ */
+private val FilterChipPadding = PaddingValues(horizontal = HorizontalElementsPadding)
+
+/**
+ * Returns the [PaddingValues] for the suggestion chip.
+ */
+private val SuggestionChipPadding = PaddingValues(horizontal = HorizontalElementsPadding)
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Switch.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Switch.kt
index a96c728..7632038 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Switch.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Switch.kt
@@ -18,25 +18,27 @@
 
 import androidx.compose.animation.core.Animatable
 import androidx.compose.animation.core.TweenSpec
-import androidx.compose.foundation.Canvas
 import androidx.compose.foundation.background
+import androidx.compose.foundation.border
 import androidx.compose.foundation.indication
 import androidx.compose.foundation.interaction.Interaction
 import androidx.compose.foundation.interaction.InteractionSource
 import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.interaction.collectIsPressedAsState
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.BoxScope
-import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.offset
-import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.requiredSize
+import androidx.compose.foundation.layout.width
 import androidx.compose.foundation.layout.wrapContentSize
 import androidx.compose.foundation.selection.toggleable
 import androidx.compose.material.ripple.rememberRipple
 import androidx.compose.material3.tokens.SwitchTokens
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.Immutable
+import androidx.compose.runtime.SideEffect
 import androidx.compose.runtime.Stable
 import androidx.compose.runtime.State
 import androidx.compose.runtime.getValue
@@ -45,56 +47,76 @@
 import androidx.compose.runtime.rememberUpdatedState
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.shadow
-import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.Shape
-import androidx.compose.ui.graphics.StrokeCap
 import androidx.compose.ui.graphics.compositeOver
-import androidx.compose.ui.graphics.drawscope.DrawScope
 import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.dp
 import kotlin.math.roundToInt
 import kotlinx.coroutines.launch
 
 /**
- * Material Design switch toggles the state of a single item on or off.
+ * <a href="https://m3.material.io/components/switch" class="external" target="_blank">Material Design Switch</a>.
+ *
+ * Switches toggle the state of a single item on or off.
+ *
+ * ![Switch image](https://developer.android.com/images/reference/androidx/compose/material3/switch.png)
  *
  * @sample androidx.compose.material3.samples.SwitchSample
  *
+ * Switch can be used with a custom icon via [thumbContent] parameter
+ *
+ * @sample androidx.compose.material3.samples.SwitchWithThumbIconSample
+ *
  * @param checked whether or not this component is checked
  * @param onCheckedChange callback to be invoked when Switch is being clicked,
  * therefore the change of checked state is requested.  If null, then this is passive
  * and relies entirely on a higher-level component to control the "checked" state.
  * @param modifier Modifier to be applied to the switch layout
+ * @param thumbContent content that will be drawn inside the thumb, expected to measure
+ * [SwitchDefaults.IconSize]
  * @param enabled whether the component is enabled or grayed out
  * @param interactionSource the [MutableInteractionSource] representing the stream of
  * [Interaction]s for this Switch. You can create and pass in your own remembered
  * [MutableInteractionSource] if you want to observe [Interaction]s and customize the
  * appearance / behavior of this Switch in different [Interaction]s.
- * @param colors [SwitchColors] that will be used to determine the color of the thumb and track
- * in different states. See [SwitchDefaults.colors].
+ * @param colors [SwitchColors] that will be used to determine the color of the thumb, track,
+ * and border in different states. See [SwitchDefaults.colors].
  */
 // TODO: b/223858692 add m.io documentation
 @Composable
-@OptIn(ExperimentalMaterial3Api::class)
+@Suppress("ComposableLambdaParameterNaming", "ComposableLambdaParameterPosition")
 fun Switch(
     checked: Boolean,
     onCheckedChange: ((Boolean) -> Unit)?,
     modifier: Modifier = Modifier,
+    thumbContent: (@Composable () -> Unit)? = null,
     enabled: Boolean = true,
     interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
-    colors: SwitchColors = SwitchDefaults.colors()
+    colors: SwitchColors = SwitchDefaults.colors(),
 ) {
-    val minBound = 0f
+    val uncheckedThumbDiameter = if (thumbContent == null) {
+        UncheckedThumbDiameter
+    } else {
+        ThumbDiameter
+    }
+
+    val thumbPaddingStart = (SwitchHeight - uncheckedThumbDiameter) / 2
+    val minBound = with(LocalDensity.current) { thumbPaddingStart.toPx() }
     val maxBound = with(LocalDensity.current) { ThumbPathLength.toPx() }
     val valueToOffset = remember<(Boolean) -> Float>(minBound, maxBound) {
         { value -> if (value) maxBound else minBound }
     }
 
     val offset = remember { Animatable(valueToOffset(checked)) }
+    SideEffect {
+        // min bound might have changed if the icon is only rendered in checked state.
+        offset.updateBounds(lowerBound = minBound)
+    }
+
     val scope = rememberCoroutineScope()
     // TODO: Add Swipeable modifier b/223797571
     val toggleableModifier =
@@ -123,7 +145,6 @@
             )
             .then(toggleableModifier)
             .wrapContentSize(Alignment.Center)
-            .padding(DefaultSwitchPadding)
             .requiredSize(SwitchWidth, SwitchHeight)
     ) {
         SwitchImpl(
@@ -132,7 +153,11 @@
             colors = colors,
             thumbValue = offset.asState(),
             interactionSource = interactionSource,
-            handleShape = SwitchTokens.HandleShape.toShape()
+            thumbShape = SwitchTokens.HandleShape.toShape(),
+            uncheckedThumbDiameter = uncheckedThumbDiameter,
+            minBound = thumbPaddingStart,
+            maxBound = ThumbPathLength,
+            thumbContent = thumbContent,
         )
     }
 }
@@ -163,63 +188,108 @@
      */
     @Composable
     fun trackColor(enabled: Boolean, checked: Boolean): State<Color>
+
+    /**
+     * Represents the color used for the switch's border, depending on [enabled] and [checked].
+     *
+     * @param enabled whether the [Switch] is enabled or not
+     * @param checked whether the [Switch] is checked or not
+     */
+    @Composable
+    fun borderColor(enabled: Boolean, checked: Boolean): State<Color>
+
+    /**
+     * Represents the content color passed to the icon if used
+     *
+     * @param enabled whether the [Switch] is enabled or not
+     * @param checked whether the [Switch] is checked or not
+     */
+    @Composable
+    fun iconColor(enabled: Boolean, checked: Boolean): State<Color>
 }
 
 @Composable
+@Suppress("ComposableLambdaParameterNaming", "ComposableLambdaParameterPosition")
 private fun BoxScope.SwitchImpl(
     checked: Boolean,
     enabled: Boolean,
     colors: SwitchColors,
     thumbValue: State<Float>,
+    thumbContent: (@Composable () -> Unit)?,
     interactionSource: InteractionSource,
-    handleShape: Shape,
+    thumbShape: Shape,
+    uncheckedThumbDiameter: Dp,
+    minBound: Dp,
+    maxBound: Dp,
 ) {
-    val elevation = if (enabled) ThumbDefaultElevation else ThumbDisabledElevation
     val trackColor by colors.trackColor(enabled, checked)
-    Canvas(Modifier.align(Alignment.Center).fillMaxSize()) {
-        drawTrack(trackColor, TrackWidth.toPx(), TrackStrokeWidth.toPx())
+    val isPressed by interactionSource.collectIsPressedAsState()
+
+    val thumbValueDp = with(LocalDensity.current) { thumbValue.value.toDp() }
+    val thumbSizeDp = if (isPressed) {
+        SwitchTokens.PressedHandleWidth
+    } else {
+        uncheckedThumbDiameter + (ThumbDiameter - uncheckedThumbDiameter) *
+            ((thumbValueDp - minBound) / (maxBound - minBound))
     }
-    val thumbColor by colors.thumbColor(enabled, checked)
-    val resolvedThumbColor = thumbColor
-    Spacer(
-        Modifier
-            .align(Alignment.CenterStart)
-            .offset { IntOffset(thumbValue.value.roundToInt(), 0) }
-            .indication(
-                interactionSource = interactionSource,
-                indication = rememberRipple(bounded = false, radius = ThumbRippleRadius)
-            )
-            .requiredSize(ThumbDiameter)
-            .shadow(elevation, handleShape, clip = false)
-            .background(resolvedThumbColor, handleShape)
-    )
+
+    val thumbOffset = if (isPressed) {
+        with(LocalDensity.current) {
+            if (checked) {
+                ThumbPathLength - SwitchTokens.TrackOutlineWidth
+            } else {
+                SwitchTokens.TrackOutlineWidth
+            }.toPx()
+        }
+    } else {
+        thumbValue.value
+    }
+
+    val trackShape = SwitchTokens.TrackShape.toShape()
+    val modifier = Modifier.align(Alignment.Center)
+        .width(SwitchWidth)
+        .height(SwitchHeight)
+        .border(
+            SwitchTokens.TrackOutlineWidth,
+            colors.borderColor(enabled, checked).value,
+            trackShape
+        )
+        .background(trackColor, trackShape)
+
+    Box(modifier) {
+        val thumbColor by colors.thumbColor(enabled, checked)
+        val resolvedThumbColor = thumbColor
+        Box(
+            modifier = Modifier
+                .align(Alignment.CenterStart)
+                .offset { IntOffset(thumbOffset.roundToInt(), 0) }
+                .indication(
+                    interactionSource = interactionSource,
+                    indication = rememberRipple(bounded = false, SwitchTokens.StateLayerSize / 2)
+                )
+                .requiredSize(thumbSizeDp)
+                .background(resolvedThumbColor, thumbShape),
+            contentAlignment = Alignment.Center
+        ) {
+            if (thumbContent != null) {
+                val iconColor = colors.iconColor(enabled, checked)
+                CompositionLocalProvider(
+                    LocalContentColor provides iconColor.value,
+                    content = thumbContent
+                )
+            }
+        }
+    }
 }
 
-private fun DrawScope.drawTrack(trackColor: Color, trackWidth: Float, strokeWidth: Float) {
-    val strokeRadius = strokeWidth / 2
-    drawLine(
-        trackColor,
-        Offset(strokeRadius, center.y),
-        Offset(trackWidth - strokeRadius, center.y),
-        strokeWidth,
-        StrokeCap.Round
-    )
-}
-
-internal val TrackWidth = SwitchTokens.TrackWidth
-internal val TrackStrokeWidth = SwitchTokens.TrackHeight
-internal val ThumbDiameter = SwitchTokens.HandleWidth
-
-private val ThumbRippleRadius = SwitchTokens.StateLayerSize / 2
-
-private val DefaultSwitchPadding = 2.dp
-private val SwitchWidth = TrackWidth
-private val SwitchHeight = ThumbDiameter
-private val ThumbPathLength = TrackWidth - ThumbDiameter
+internal val ThumbDiameter = SwitchTokens.SelectedHandleWidth
+internal val UncheckedThumbDiameter = SwitchTokens.UnselectedHandleWidth
+private val SwitchWidth = SwitchTokens.TrackWidth
+private val SwitchHeight = SwitchTokens.TrackHeight
+private val ThumbPadding = (SwitchHeight - ThumbDiameter) / 2
+private val ThumbPathLength = (SwitchWidth - ThumbDiameter) - ThumbPadding
 
 private val AnimationSpec = TweenSpec<Float>(durationMillis = 100)
-private val ThumbDefaultElevation = SwitchTokens.HandleElevation
-private val ThumbDisabledElevation = SwitchTokens.DisabledHandleElevation
 
 /**
  * Contains the default values used by [Switch]
@@ -231,43 +301,77 @@
      *
      * @param checkedThumbColor the color used for the thumb when enabled and checked
      * @param checkedTrackColor the color used for the track when enabled and checked
-     * [disabledCheckedTrackColor]
+     * @param checkedBorderColor the color used for the border when enabled and checked
+     * @param checkedIconColor the color used for the icon when enabled and checked
      * @param uncheckedThumbColor the color used for the thumb when enabled and unchecked
      * @param uncheckedTrackColor the color used for the track when enabled and unchecked
-     * [disabledUncheckedTrackColor]
+     * @param uncheckedBorderColor the color used for the border when enabled and unchecked
+     * @param uncheckedIconColor the color used for the icon when enabled and unchecked
      * @param disabledCheckedThumbColor the color used for the thumb when disabled and checked
      * @param disabledCheckedTrackColor the color used for the track when disabled and checked
+     * @param disabledCheckedBorderColor the color used for the border when disabled and checked
+     * @param disabledCheckedIconColor the color used for the icon when disabled and checked
      * @param disabledUncheckedThumbColor the color used for the thumb when disabled and unchecked
      * @param disabledUncheckedTrackColor the color used for the track when disabled and unchecked
+     * @param disabledUncheckedBorderColor the color used for the border when disabled and unchecked
+     * @param disabledUncheckedIconColor the color used for the icon when disabled and unchecked
      */
     @Composable
     fun colors(
         checkedThumbColor: Color = SwitchTokens.SelectedHandleColor.toColor(),
         checkedTrackColor: Color = SwitchTokens.SelectedTrackColor.toColor(),
+        checkedBorderColor: Color = Color.Transparent,
+        checkedIconColor: Color = SwitchTokens.SelectedIconColor.toColor(),
         uncheckedThumbColor: Color = SwitchTokens.UnselectedHandleColor.toColor(),
         uncheckedTrackColor: Color = SwitchTokens.UnselectedTrackColor.toColor(),
+        uncheckedBorderColor: Color = SwitchTokens.UnselectedFocusTrackOutlineColor.toColor(),
+        uncheckedIconColor: Color = SwitchTokens.UnselectedIconColor.toColor(),
         disabledCheckedThumbColor: Color = checkedThumbColor
-            .copy(alpha = SwitchTokens.DisabledHandleOpacity)
+            .copy(alpha = SwitchTokens.DisabledSelectedHandleOpacity)
             .compositeOver(MaterialTheme.colorScheme.surface),
         disabledCheckedTrackColor: Color = checkedTrackColor
             .copy(alpha = SwitchTokens.DisabledTrackOpacity)
             .compositeOver(MaterialTheme.colorScheme.surface),
+        disabledCheckedBorderColor: Color = Color.Transparent,
+        disabledCheckedIconColor: Color = SwitchTokens.DisabledSelectedIconColor.toColor()
+            .copy(alpha = SwitchTokens.DisabledSelectedIconOpacity)
+            .compositeOver(MaterialTheme.colorScheme.surface),
         disabledUncheckedThumbColor: Color = uncheckedThumbColor
-            .copy(alpha = SwitchTokens.DisabledHandleOpacity)
+            .copy(alpha = SwitchTokens.DisabledSelectedHandleOpacity)
             .compositeOver(MaterialTheme.colorScheme.surface),
         disabledUncheckedTrackColor: Color = uncheckedTrackColor
             .copy(alpha = SwitchTokens.DisabledTrackOpacity)
-            .compositeOver(MaterialTheme.colorScheme.surface)
+            .compositeOver(MaterialTheme.colorScheme.surface),
+        disabledUncheckedBorderColor: Color =
+            SwitchTokens.DisabledUnselectedTrackOutlineColor.toColor()
+                .copy(alpha = SwitchTokens.DisabledTrackOpacity)
+                .compositeOver(MaterialTheme.colorScheme.surface),
+        disabledUncheckedIconColor: Color = SwitchTokens.DisabledUnselectedIconColor.toColor()
+            .copy(alpha = SwitchTokens.DisabledUnselectedIconOpacity)
+            .compositeOver(MaterialTheme.colorScheme.surface),
     ): SwitchColors = DefaultSwitchColors(
         checkedThumbColor = checkedThumbColor,
         checkedTrackColor = checkedTrackColor,
+        checkedBorderColor = checkedBorderColor,
+        checkedIconColor = checkedIconColor,
         uncheckedThumbColor = uncheckedThumbColor,
         uncheckedTrackColor = uncheckedTrackColor,
+        uncheckedBorderColor = uncheckedBorderColor,
+        uncheckedIconColor = uncheckedIconColor,
         disabledCheckedThumbColor = disabledCheckedThumbColor,
         disabledCheckedTrackColor = disabledCheckedTrackColor,
+        disabledCheckedBorderColor = disabledCheckedBorderColor,
+        disabledCheckedIconColor = disabledCheckedIconColor,
         disabledUncheckedThumbColor = disabledUncheckedThumbColor,
-        disabledUncheckedTrackColor = disabledUncheckedTrackColor
+        disabledUncheckedTrackColor = disabledUncheckedTrackColor,
+        disabledUncheckedBorderColor = disabledUncheckedBorderColor,
+        disabledUncheckedIconColor = disabledUncheckedIconColor
     )
+
+    /**
+     * Icon size to use for `thumbContent`
+     */
+    val IconSize = 16.dp
 }
 
 /**
@@ -277,12 +381,20 @@
 private class DefaultSwitchColors(
     private val checkedThumbColor: Color,
     private val checkedTrackColor: Color,
+    private val checkedBorderColor: Color,
+    private val checkedIconColor: Color,
     private val uncheckedThumbColor: Color,
     private val uncheckedTrackColor: Color,
+    private val uncheckedBorderColor: Color,
+    private val uncheckedIconColor: Color,
     private val disabledCheckedThumbColor: Color,
     private val disabledCheckedTrackColor: Color,
+    private val disabledCheckedBorderColor: Color,
+    private val disabledCheckedIconColor: Color,
     private val disabledUncheckedThumbColor: Color,
-    private val disabledUncheckedTrackColor: Color
+    private val disabledUncheckedTrackColor: Color,
+    private val disabledUncheckedBorderColor: Color,
+    private val disabledUncheckedIconColor: Color
 ) : SwitchColors {
     @Composable
     override fun thumbColor(enabled: Boolean, checked: Boolean): State<Color> {
@@ -306,6 +418,28 @@
         )
     }
 
+    @Composable
+    override fun borderColor(enabled: Boolean, checked: Boolean): State<Color> {
+        return rememberUpdatedState(
+            if (enabled) {
+                if (checked) checkedBorderColor else uncheckedBorderColor
+            } else {
+                if (checked) disabledCheckedBorderColor else disabledUncheckedBorderColor
+            }
+        )
+    }
+
+    @Composable
+    override fun iconColor(enabled: Boolean, checked: Boolean): State<Color> {
+        return rememberUpdatedState(
+            if (enabled) {
+                if (checked) checkedIconColor else uncheckedIconColor
+            } else {
+                if (checked) disabledCheckedIconColor else disabledUncheckedIconColor
+            }
+        )
+    }
+
     override fun equals(other: Any?): Boolean {
         if (this === other) return true
         if (other == null || this::class != other::class) return false
@@ -314,12 +448,20 @@
 
         if (checkedThumbColor != other.checkedThumbColor) return false
         if (checkedTrackColor != other.checkedTrackColor) return false
+        if (checkedBorderColor != other.checkedBorderColor) return false
+        if (checkedIconColor != other.checkedIconColor) return false
         if (uncheckedThumbColor != other.uncheckedThumbColor) return false
         if (uncheckedTrackColor != other.uncheckedTrackColor) return false
+        if (uncheckedBorderColor != other.uncheckedBorderColor) return false
+        if (uncheckedIconColor != other.uncheckedIconColor) return false
         if (disabledCheckedThumbColor != other.disabledCheckedThumbColor) return false
         if (disabledCheckedTrackColor != other.disabledCheckedTrackColor) return false
+        if (disabledCheckedBorderColor != other.disabledCheckedBorderColor) return false
+        if (disabledCheckedIconColor != other.disabledCheckedIconColor) return false
         if (disabledUncheckedThumbColor != other.disabledUncheckedThumbColor) return false
         if (disabledUncheckedTrackColor != other.disabledUncheckedTrackColor) return false
+        if (disabledUncheckedBorderColor != other.disabledUncheckedBorderColor) return false
+        if (disabledUncheckedIconColor != other.disabledUncheckedIconColor) return false
 
         return true
     }
@@ -327,12 +469,20 @@
     override fun hashCode(): Int {
         var result = checkedThumbColor.hashCode()
         result = 31 * result + checkedTrackColor.hashCode()
+        result = 31 * result + checkedBorderColor.hashCode()
+        result = 31 * result + checkedIconColor.hashCode()
         result = 31 * result + uncheckedThumbColor.hashCode()
         result = 31 * result + uncheckedTrackColor.hashCode()
+        result = 31 * result + uncheckedBorderColor.hashCode()
+        result = 31 * result + uncheckedIconColor.hashCode()
         result = 31 * result + disabledCheckedThumbColor.hashCode()
         result = 31 * result + disabledCheckedTrackColor.hashCode()
+        result = 31 * result + disabledCheckedBorderColor.hashCode()
+        result = 31 * result + disabledCheckedIconColor.hashCode()
         result = 31 * result + disabledUncheckedThumbColor.hashCode()
         result = 31 * result + disabledUncheckedTrackColor.hashCode()
+        result = 31 * result + disabledUncheckedBorderColor.hashCode()
+        result = 31 * result + disabledUncheckedIconColor.hashCode()
         return result
     }
 }
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/AssistChipTokens.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/AssistChipTokens.kt
new file mode 100644
index 0000000..481b9e8e
--- /dev/null
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/AssistChipTokens.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+// VERSION: v0_92
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+package androidx.compose.material3.tokens
+
+import androidx.compose.ui.unit.dp
+
+internal object AssistChipTokens {
+    val ContainerHeight = 32.0.dp
+    val ContainerShape = ShapeKeyTokens.CornerSmall
+    val ContainerSurfaceTintLayerColor = ColorSchemeKeyTokens.SurfaceTint
+    val DisabledLabelTextColor = ColorSchemeKeyTokens.OnSurface
+    const val DisabledLabelTextOpacity = 0.38f
+    val DraggedContainerElevation = ElevationTokens.Level4
+    val DraggedLabelTextColor = ColorSchemeKeyTokens.OnSurface
+    val ElevatedContainerColor = ColorSchemeKeyTokens.Surface
+    val ElevatedContainerElevation = ElevationTokens.Level1
+    val ElevatedDisabledContainerColor = ColorSchemeKeyTokens.OnSurface
+    val ElevatedDisabledContainerElevation = ElevationTokens.Level0
+    const val ElevatedDisabledContainerOpacity = 0.12f
+    val ElevatedFocusContainerElevation = ElevationTokens.Level1
+    val ElevatedHoverContainerElevation = ElevationTokens.Level2
+    val ElevatedPressedContainerElevation = ElevationTokens.Level1
+    val FlatContainerElevation = ElevationTokens.Level0
+    val FlatDisabledOutlineColor = ColorSchemeKeyTokens.OnSurface
+    const val FlatDisabledOutlineOpacity = 0.12f
+    val FlatFocusOutlineColor = ColorSchemeKeyTokens.OnSurface
+    val FlatOutlineColor = ColorSchemeKeyTokens.Outline
+    val FlatOutlineWidth = 1.0.dp
+    val FocusLabelTextColor = ColorSchemeKeyTokens.OnSurface
+    val HoverLabelTextColor = ColorSchemeKeyTokens.OnSurface
+    val LabelTextColor = ColorSchemeKeyTokens.OnSurface
+    val LabelTextFont = TypographyKeyTokens.LabelLarge
+    val PressedLabelTextColor = ColorSchemeKeyTokens.OnSurface
+    val DisabledIconColor = ColorSchemeKeyTokens.OnSurface
+    const val DisabledIconOpacity = 0.38f
+    val DraggedIconColor = ColorSchemeKeyTokens.Primary
+    val FocusIconColor = ColorSchemeKeyTokens.Primary
+    val HoverIconColor = ColorSchemeKeyTokens.Primary
+    val IconColor = ColorSchemeKeyTokens.Primary
+    val IconSize = 18.0.dp
+    val PressedIconColor = ColorSchemeKeyTokens.Primary
+}
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/FilterChipTokens.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/FilterChipTokens.kt
new file mode 100644
index 0000000..b88f808
--- /dev/null
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/FilterChipTokens.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+// VERSION: v0_92
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+package androidx.compose.material3.tokens
+
+import androidx.compose.ui.unit.dp
+
+internal object FilterChipTokens {
+    val ContainerHeight = 32.0.dp
+    val ContainerShape = ShapeKeyTokens.CornerSmall
+    val ContainerSurfaceTintLayerColor = ColorSchemeKeyTokens.SurfaceTint
+    val DisabledLabelTextColor = ColorSchemeKeyTokens.OnSurface
+    const val DisabledLabelTextOpacity = 0.38f
+    val DraggedContainerElevation = ElevationTokens.Level4
+    val ElevatedContainerElevation = ElevationTokens.Level1
+    val ElevatedDisabledContainerColor = ColorSchemeKeyTokens.OnSurface
+    val ElevatedDisabledContainerElevation = ElevationTokens.Level0
+    const val ElevatedDisabledContainerOpacity = 0.12f
+    val ElevatedFocusContainerElevation = ElevationTokens.Level1
+    val ElevatedHoverContainerElevation = ElevationTokens.Level2
+    val ElevatedPressedContainerElevation = ElevationTokens.Level1
+    val ElevatedSelectedContainerColor = ColorSchemeKeyTokens.SecondaryContainer
+    val ElevatedUnselectedContainerColor = ColorSchemeKeyTokens.Surface
+    val FlatContainerElevation = ElevationTokens.Level0
+    val FlatDisabledSelectedContainerColor = ColorSchemeKeyTokens.OnSurface
+    const val FlatDisabledSelectedContainerOpacity = 0.12f
+    val FlatDisabledUnselectedOutlineColor = ColorSchemeKeyTokens.OnSurface
+    const val FlatDisabledUnselectedOutlineOpacity = 0.12f
+    val FlatSelectedContainerColor = ColorSchemeKeyTokens.SecondaryContainer
+    val FlatSelectedFocusContainerElevation = ElevationTokens.Level0
+    val FlatSelectedHoverContainerElevation = ElevationTokens.Level1
+    val FlatSelectedOutlineWidth = 0.0.dp
+    val FlatSelectedPressedContainerElevation = ElevationTokens.Level0
+    val FlatUnselectedFocusContainerElevation = ElevationTokens.Level0
+    val FlatUnselectedFocusOutlineColor = ColorSchemeKeyTokens.OnSurfaceVariant
+    val FlatUnselectedHoverContainerElevation = ElevationTokens.Level0
+    val FlatUnselectedOutlineColor = ColorSchemeKeyTokens.Outline
+    val FlatUnselectedOutlineWidth = 1.0.dp
+    val FlatUnselectedPressedContainerElevation = ElevationTokens.Level0
+    val LabelTextFont = TypographyKeyTokens.LabelLarge
+    val SelectedDraggedLabelTextColor = ColorSchemeKeyTokens.OnSecondaryContainer
+    val SelectedFocusLabelTextColor = ColorSchemeKeyTokens.OnSecondaryContainer
+    val SelectedHoverLabelTextColor = ColorSchemeKeyTokens.OnSecondaryContainer
+    val SelectedLabelTextColor = ColorSchemeKeyTokens.OnSecondaryContainer
+    val SelectedPressedLabelTextColor = ColorSchemeKeyTokens.OnSecondaryContainer
+    val UnselectedDraggedLabelTextColor = ColorSchemeKeyTokens.OnSurfaceVariant
+    val UnselectedFocusLabelTextColor = ColorSchemeKeyTokens.OnSurfaceVariant
+    val UnselectedHoverLabelTextColor = ColorSchemeKeyTokens.OnSurfaceVariant
+    val UnselectedLabelTextColor = ColorSchemeKeyTokens.OnSurfaceVariant
+    val UnselectedPressedLabelTextColor = ColorSchemeKeyTokens.OnSurfaceVariant
+    val DisabledIconColor = ColorSchemeKeyTokens.OnSurface
+    const val DisabledIconOpacity = 0.38f
+    val IconSize = 18.0.dp
+    val SelectedDraggedIconColor = ColorSchemeKeyTokens.OnSecondaryContainer
+    val SelectedFocusIconColor = ColorSchemeKeyTokens.OnSecondaryContainer
+    val SelectedHoverIconColor = ColorSchemeKeyTokens.OnSecondaryContainer
+    val SelectedIconColor = ColorSchemeKeyTokens.OnSecondaryContainer
+    val SelectedPressedIconColor = ColorSchemeKeyTokens.OnSecondaryContainer
+    val UnselectedDraggedIconColor = ColorSchemeKeyTokens.OnSurfaceVariant
+    val UnselectedFocusIconColor = ColorSchemeKeyTokens.OnSurfaceVariant
+    val UnselectedHoverIconColor = ColorSchemeKeyTokens.OnSurfaceVariant
+    val UnselectedIconColor = ColorSchemeKeyTokens.OnSurfaceVariant
+    val UnselectedPressedIconColor = ColorSchemeKeyTokens.OnSurfaceVariant
+}
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/InputChipTokens.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/InputChipTokens.kt
new file mode 100644
index 0000000..5163010
--- /dev/null
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/InputChipTokens.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+// VERSION: v0_92
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+package androidx.compose.material3.tokens
+
+import androidx.compose.ui.unit.dp
+
+internal object InputChipTokens {
+    val ContainerElevation = ElevationTokens.Level0
+    val ContainerHeight = 32.0.dp
+    val ContainerShape = ShapeKeyTokens.CornerSmall
+    val DisabledLabelTextColor = ColorSchemeKeyTokens.OnSurface
+    const val DisabledLabelTextOpacity = 0.38f
+    val DisabledOutlineColor = ColorSchemeKeyTokens.OnSurface
+    const val DisabledOutlineOpacity = 0.12f
+    val DraggedContainerElevation = ElevationTokens.Level4
+    val DraggedLabelTextColor = ColorSchemeKeyTokens.OnSurfaceVariant
+    val FocusLabelTextColor = ColorSchemeKeyTokens.OnSurfaceVariant
+    val FocusOutlineColor = ColorSchemeKeyTokens.OnSurfaceVariant
+    val HoverLabelTextColor = ColorSchemeKeyTokens.OnSurfaceVariant
+    val LabelTextColor = ColorSchemeKeyTokens.OnSurfaceVariant
+    val LabelTextFont = TypographyKeyTokens.LabelLarge
+    val OutlineColor = ColorSchemeKeyTokens.Outline
+    val OutlineWidth = 1.0.dp
+    val PressedLabelTextColor = ColorSchemeKeyTokens.OnSurfaceVariant
+    val AvatarShape = ShapeKeyTokens.CornerFull
+    val AvatarSize = 24.0.dp
+    const val DisabledAvatarOpacity = 0.38f
+    val DisabledLeadingIconColor = ColorSchemeKeyTokens.OnSurface
+    const val DisabledLeadingIconOpacity = 0.38f
+    val DraggedLeadingIconColor = ColorSchemeKeyTokens.OnSurfaceVariant
+    val FocusLeadingIconColor = ColorSchemeKeyTokens.OnSurfaceVariant
+    val HoverLeadingIconColor = ColorSchemeKeyTokens.OnSurfaceVariant
+    val LeadingIconColor = ColorSchemeKeyTokens.OnSurfaceVariant
+    val LeadingIconSize = 18.0.dp
+    val PressedLeadingIconColor = ColorSchemeKeyTokens.OnSurfaceVariant
+    val DisabledTrailingIconColor = ColorSchemeKeyTokens.OnSurface
+    const val DisabledTrailingIconOpacity = 0.38f
+    val DraggedTrailingIconColor = ColorSchemeKeyTokens.OnSurfaceVariant
+    val FocusTrailingIconColor = ColorSchemeKeyTokens.OnSurfaceVariant
+    val HoverTrailingIconColor = ColorSchemeKeyTokens.OnSurfaceVariant
+    val PressedTrailingIconColor = ColorSchemeKeyTokens.OnSurfaceVariant
+    val TrailingIconColor = ColorSchemeKeyTokens.OnSurfaceVariant
+    val TrailingIconSize = 18.0.dp
+}
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/SuggestionChipTokens.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/SuggestionChipTokens.kt
new file mode 100644
index 0000000..7878f1d
--- /dev/null
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/SuggestionChipTokens.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+// VERSION: v0_92
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+package androidx.compose.material3.tokens
+
+import androidx.compose.ui.unit.dp
+
+internal object SuggestionChipTokens {
+    val ContainerHeight = 32.0.dp
+    val ContainerShape = ShapeKeyTokens.CornerSmall
+    val ContainerSurfaceTintLayerColor = ColorSchemeKeyTokens.SurfaceTint
+    val DisabledLabelTextColor = ColorSchemeKeyTokens.OnSurface
+    const val DisabledLabelTextOpacity = 0.38f
+    val DraggedContainerElevation = ElevationTokens.Level4
+    val DraggedLabelTextColor = ColorSchemeKeyTokens.OnSurfaceVariant
+    val ElevatedContainerColor = ColorSchemeKeyTokens.Surface
+    val ElevatedContainerElevation = ElevationTokens.Level1
+    val ElevatedDisabledContainerColor = ColorSchemeKeyTokens.OnSurface
+    val ElevatedDisabledContainerElevation = ElevationTokens.Level0
+    const val ElevatedDisabledContainerOpacity = 0.12f
+    val ElevatedFocusContainerElevation = ElevationTokens.Level1
+    val ElevatedHoverContainerElevation = ElevationTokens.Level2
+    val ElevatedPressedContainerElevation = ElevationTokens.Level1
+    val FlatContainerElevation = ElevationTokens.Level0
+    val FlatDisabledOutlineColor = ColorSchemeKeyTokens.OnSurface
+    const val FlatDisabledOutlineOpacity = 0.12f
+    val FlatFocusOutlineColor = ColorSchemeKeyTokens.OnSurfaceVariant
+    val FlatOutlineColor = ColorSchemeKeyTokens.Outline
+    val FlatOutlineWidth = 1.0.dp
+    val FocusLabelTextColor = ColorSchemeKeyTokens.OnSurfaceVariant
+    val HoverLabelTextColor = ColorSchemeKeyTokens.OnSurfaceVariant
+    val LabelTextColor = ColorSchemeKeyTokens.OnSurfaceVariant
+    val LabelTextFont = TypographyKeyTokens.LabelLarge
+    val PressedLabelTextColor = ColorSchemeKeyTokens.OnSurfaceVariant
+}
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/SwitchTokens.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/SwitchTokens.kt
index ae26335..a0e061dd 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/SwitchTokens.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/SwitchTokens.kt
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// VERSION: v0_92
+// VERSION: v0_93
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.compose.material3.tokens
@@ -21,43 +21,60 @@
 import androidx.compose.ui.unit.dp
 
 internal object SwitchTokens {
-    val DisabledHandleElevation = ElevationTokens.Level0
-    const val DisabledHandleOpacity = 0.38f
-    val DisabledSelectedHandleColor = ColorSchemeKeyTokens.OnSurface
-    val DisabledSelectedIconColor = ColorSchemeKeyTokens.OnPrimary
+    val DisabledSelectedHandleColor = ColorSchemeKeyTokens.Surface
+    const val DisabledSelectedHandleOpacity = 1.0f
+    val DisabledSelectedIconColor = ColorSchemeKeyTokens.OnSurface
     const val DisabledSelectedIconOpacity = 0.38f
     val DisabledSelectedTrackColor = ColorSchemeKeyTokens.OnSurface
     const val DisabledTrackOpacity = 0.12f
     val DisabledUnselectedHandleColor = ColorSchemeKeyTokens.OnSurface
-    val DisabledUnselectedIconColor = ColorSchemeKeyTokens.OnPrimary
+    const val DisabledUnselectedHandleOpacity = 0.38f
+    val DisabledUnselectedIconColor = ColorSchemeKeyTokens.SurfaceVariant
     const val DisabledUnselectedIconOpacity = 0.38f
-    val DisabledUnselectedTrackColor = ColorSchemeKeyTokens.OnSurface
-    val HandleElevation = ElevationTokens.Level1
-    val HandleHeight = 20.0.dp
+    val DisabledUnselectedTrackColor = ColorSchemeKeyTokens.SurfaceVariant
+    val DisabledUnselectedTrackOutlineColor = ColorSchemeKeyTokens.OnSurface
     val HandleShape = ShapeKeyTokens.CornerFull
-    val HandleWidth = 20.0.dp
-    val SelectedFocusHandleColor = ColorSchemeKeyTokens.Primary
-    val SelectedFocusTrackColor = ColorSchemeKeyTokens.PrimaryContainer
-    val SelectedHandleColor = ColorSchemeKeyTokens.Primary
-    val SelectedHoverHandleColor = ColorSchemeKeyTokens.Primary
-    val SelectedHoverTrackColor = ColorSchemeKeyTokens.PrimaryContainer
-    val SelectedIconColor = ColorSchemeKeyTokens.OnPrimary
-    val SelectedIconSize = 18.0.dp
-    val SelectedPressedHandleColor = ColorSchemeKeyTokens.Primary
-    val SelectedPressedTrackColor = ColorSchemeKeyTokens.PrimaryContainer
-    val SelectedTrackColor = ColorSchemeKeyTokens.PrimaryContainer
+    val PressedHandleHeight = 28.0.dp
+    val PressedHandleWidth = 28.0.dp
+    val SelectedFocusHandleColor = ColorSchemeKeyTokens.PrimaryContainer
+    val SelectedFocusIconColor = ColorSchemeKeyTokens.OnPrimaryContainer
+    val SelectedFocusTrackColor = ColorSchemeKeyTokens.Primary
+    val SelectedHandleColor = ColorSchemeKeyTokens.OnPrimary
+    val SelectedHandleHeight = 24.0.dp
+    val SelectedHandleWidth = 24.0.dp
+    val SelectedHoverHandleColor = ColorSchemeKeyTokens.PrimaryContainer
+    val SelectedHoverIconColor = ColorSchemeKeyTokens.OnPrimaryContainer
+    val SelectedHoverTrackColor = ColorSchemeKeyTokens.Primary
+    val SelectedIconColor = ColorSchemeKeyTokens.OnPrimaryContainer
+    val SelectedIconSize = 16.0.dp
+    val SelectedPressedHandleColor = ColorSchemeKeyTokens.PrimaryContainer
+    val SelectedPressedIconColor = ColorSchemeKeyTokens.OnPrimaryContainer
+    val SelectedPressedTrackColor = ColorSchemeKeyTokens.Primary
+    val SelectedTrackColor = ColorSchemeKeyTokens.Primary
+    val StateLayerShape = ShapeKeyTokens.CornerFull
     val StateLayerSize = 40.0.dp
-    val TrackHeight = 14.0.dp
+    val TrackHeight = 32.0.dp
+    val TrackOutlineWidth = 2.0.dp
     val TrackShape = ShapeKeyTokens.CornerFull
-    val TrackWidth = 36.0.dp
-    val UnselectedFocusHandleColor = ColorSchemeKeyTokens.OnSurface
-    val UnselectedFocusTrackColor = ColorSchemeKeyTokens.Outline
-    val UnselectedHandleColor = ColorSchemeKeyTokens.OnSurface
-    val UnselectedHoverHandleColor = ColorSchemeKeyTokens.OnSurface
-    val UnselectedHoverTrackColor = ColorSchemeKeyTokens.Outline
-    val UnselectedIconColor = ColorSchemeKeyTokens.OnPrimary
-    val UnselectedIconSize = 18.0.dp
-    val UnselectedPressedHandleColor = ColorSchemeKeyTokens.OnSurface
-    val UnselectedPressedTrackColor = ColorSchemeKeyTokens.Outline
-    val UnselectedTrackColor = ColorSchemeKeyTokens.Outline
+    val TrackWidth = 52.0.dp
+    val UnselectedFocusHandleColor = ColorSchemeKeyTokens.OnSurfaceVariant
+    val UnselectedFocusIconColor = ColorSchemeKeyTokens.SurfaceVariant
+    val UnselectedFocusTrackColor = ColorSchemeKeyTokens.SurfaceVariant
+    val UnselectedFocusTrackOutlineColor = ColorSchemeKeyTokens.Outline
+    val UnselectedHandleColor = ColorSchemeKeyTokens.Outline
+    val UnselectedHandleHeight = 16.0.dp
+    val UnselectedHandleWidth = 16.0.dp
+    val UnselectedHoverHandleColor = ColorSchemeKeyTokens.OnSurfaceVariant
+    val UnselectedHoverIconColor = ColorSchemeKeyTokens.SurfaceVariant
+    val UnselectedHoverTrackColor = ColorSchemeKeyTokens.SurfaceVariant
+    val UnselectedHoverTrackOutlineColor = ColorSchemeKeyTokens.Outline
+    val UnselectedIconColor = ColorSchemeKeyTokens.SurfaceVariant
+    val UnselectedIconSize = 16.0.dp
+    val UnselectedPressedHandleColor = ColorSchemeKeyTokens.OnSurfaceVariant
+    val UnselectedPressedIconColor = ColorSchemeKeyTokens.SurfaceVariant
+    val UnselectedPressedTrackColor = ColorSchemeKeyTokens.SurfaceVariant
+    val UnselectedPressedTrackOutlineColor = ColorSchemeKeyTokens.Outline
+    val UnselectedTrackColor = ColorSchemeKeyTokens.SurfaceVariant
+    val IconHandleHeight = 24.0.dp
+    val IconHandleWidth = 24.0.dp
 }
\ No newline at end of file
diff --git a/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/Actions.kt b/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/Actions.kt
index a34bf5f..31a8848 100644
--- a/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/Actions.kt
+++ b/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/Actions.kt
@@ -275,6 +275,14 @@
  * execution of [block] or injection of the events, all (subsequent) events are dropped and the
  * error is thrown here.
  *
+ * Due to the batching of events, all events in a block are sent together and no recomposition will
+ * take place in between events. Additionally all events will be generated before any of the events
+ * take effect. This means that the screen coordinates of all events are resolved before any of
+ * the events can cause the position of the node being injected into to change. This has certain
+ * advantages, for example, in the cases of nested scrolling or dragging an element around, it
+ * prevents the injection of events into a moving target since all events are enqueued before any
+ * of them has taken effect.
+ *
  * Example of performing a click:
  * @sample androidx.compose.ui.test.samples.gestureClick
  *
@@ -318,6 +326,14 @@
  * execution of [block] or injection of the events, all (subsequent) events are dropped and the
  * error is thrown here.
  *
+ * Due to the batching of events, all events in a block are sent together and no recomposition will
+ * take place in between events. Additionally all events will be generated before any of the events
+ * take effect. This means that the screen coordinates of all events are resolved before any of
+ * the events can cause the position of the node being injected into to change. This has certain
+ * advantages, for example, in the cases of nested scrolling or dragging an element around, it
+ * prevents the injection of events into a moving target since all events are enqueued before any
+ * of them has taken effect.
+ *
  * Example of performing a swipe up:
  * @sample androidx.compose.ui.test.samples.touchInputSwipeUp
  *
@@ -364,6 +380,14 @@
  * execution of [block] or injection of the events, all (subsequent) events are dropped and the
  * error is thrown here.
  *
+ * Due to the batching of events, all events in a block are sent together and no recomposition will
+ * take place in between events. Additionally all events will be generated before any of the events
+ * take effect. This means that the screen coordinates of all events are resolved before any of
+ * the events can cause the position of the node being injected into to change. This has certain
+ * advantages, for example, in the cases of nested scrolling or dragging an element around, it
+ * prevents the injection of events into a moving target since all events are enqueued before any
+ * of them has taken effect.
+ *
  * Example of performing a mouse click:
  * @sample androidx.compose.ui.test.samples.mouseInputClick
  *
@@ -411,6 +435,14 @@
  * execution of [block] or injection of the events, all (subsequent) events are dropped and the
  * error is thrown here.
  *
+ * Due to the batching of events, all events in a block are sent together and no recomposition will
+ * take place in between events. Additionally all events will be generated before any of the events
+ * take effect. This means that the screen coordinates of all events are resolved before any of
+ * the events can cause the position of the node being injected into to change. This has certain
+ * advantages, for example, in the cases of nested scrolling or dragging an element around, it
+ * prevents the injection of events into a moving target since all events are enqueued before any
+ * of them has taken effect.
+ *
  * @param block A lambda with [MultiModalInjectionScope] as receiver that describes the gesture
  * by sending all multi modal events.
  * @return The [SemanticsNodeInteraction] that is the receiver of this method
diff --git a/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/TouchInjectionScope.kt b/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/TouchInjectionScope.kt
index 4a70526..28ad737 100644
--- a/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/TouchInjectionScope.kt
+++ b/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/TouchInjectionScope.kt
@@ -57,7 +57,13 @@
  * [performTouchInput] has executed its code block. Because gestures don't have to be defined all
  * in the same [performTouchInput] block, keep in mind that while the gesture is not complete,
  * all code you execute in between these blocks will be executed while imaginary fingers are
- * actively touching the screen.
+ * actively touching the screen. The events sent as part of the same batch will not be interrupted
+ * by recomposition, however, if a gesture spans multiple [performTouchInput] blocks it is
+ * important to remember that recomposition, layout and drawing could take place during the
+ * gesture, which may lead to events being injected into a moving target. As pointer positions are
+ * manipulated in the current node's local coordinate system, this could lead to issues caused by
+ * the fact that part of the gesture will take effect before the rest of the events have been
+ * enqueued.
  *
  * Example of performing a click:
  * @sample androidx.compose.ui.test.samples.touchInputClick
diff --git a/compose/ui/ui/api/public_plus_experimental_current.txt b/compose/ui/ui/api/public_plus_experimental_current.txt
index 90b2428..1aebd5a 100644
--- a/compose/ui/ui/api/public_plus_experimental_current.txt
+++ b/compose/ui/ui/api/public_plus_experimental_current.txt
@@ -2578,7 +2578,7 @@
   }
 
   public final class NestedScrollInteropConnectionKt {
-    method @androidx.compose.runtime.Composable @androidx.compose.ui.ExperimentalComposeUiApi public static androidx.compose.ui.input.nestedscroll.NestedScrollConnection rememberNestedScrollInteropConnection(androidx.compose.ui.platform.ComposeView view);
+    method @androidx.compose.runtime.Composable @androidx.compose.ui.ExperimentalComposeUiApi public static androidx.compose.ui.input.nestedscroll.NestedScrollConnection rememberNestedScrollInteropConnection();
   }
 
   public final class ShapeContainingUtilKt {
diff --git a/compose/ui/ui/integration-tests/ui-demos/src/main/AndroidManifest.xml b/compose/ui/ui/integration-tests/ui-demos/src/main/AndroidManifest.xml
index a0f7c783..d853515 100644
--- a/compose/ui/ui/integration-tests/ui-demos/src/main/AndroidManifest.xml
+++ b/compose/ui/ui/integration-tests/ui-demos/src/main/AndroidManifest.xml
@@ -49,5 +49,10 @@
             android:name=".viewinterop.ComposeInAndroidCoordinatorLayout"
             android:configChanges="orientation|screenSize"
             android:label="Compose in Coordinator Layout Activity" />
+
+        <activity
+            android:name=".viewinterop.ViewComposeViewNestedScrollInteropDemo"
+            android:configChanges="orientation|screenSize"
+            android:label="Coordinator Layout Activity with Compose > View" />
     </application>
 </manifest>
\ No newline at end of file
diff --git a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/UiDemos.kt b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/UiDemos.kt
index 9f0ae04..528f87d 100644
--- a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/UiDemos.kt
+++ b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/UiDemos.kt
@@ -17,12 +17,15 @@
 package androidx.compose.ui.demos
 
 import androidx.compose.foundation.demos.text.SoftwareKeyboardControllerDemo
+import androidx.compose.integration.demos.common.ActivityDemo
 import androidx.compose.integration.demos.common.ComposableDemo
 import androidx.compose.integration.demos.common.DemoCategory
+import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.demos.autofill.ExplicitAutofillTypesDemo
 import androidx.compose.ui.demos.focus.AdjacentScrollablesFocusDemo
 import androidx.compose.ui.demos.focus.CaptureFocusDemo
 import androidx.compose.ui.demos.focus.ClickableInLazyColumnDemo
+import androidx.compose.ui.demos.focus.ConditionalFocusabilityDemo
 import androidx.compose.ui.demos.focus.CustomFocusOrderDemo
 import androidx.compose.ui.demos.focus.FocusInDialogDemo
 import androidx.compose.ui.demos.focus.FocusInPopupDemo
@@ -55,18 +58,21 @@
 import androidx.compose.ui.demos.gestures.ScrollGestureFilterDemo
 import androidx.compose.ui.demos.gestures.VerticalScrollerInDrawerDemo
 import androidx.compose.ui.demos.input.TouchModeDemo
-import androidx.compose.ui.demos.focus.ConditionalFocusabilityDemo
-import androidx.compose.ui.demos.keyinput.KeyInputDemo
 import androidx.compose.ui.demos.keyinput.InterceptEnterToSendMessageDemo
+import androidx.compose.ui.demos.keyinput.KeyInputDemo
 import androidx.compose.ui.demos.modifier.CommunicatingModifierDemo
+import androidx.compose.ui.demos.recyclerview.RecyclerViewDemos
 import androidx.compose.ui.demos.viewinterop.AndroidInComposeDemos
 import androidx.compose.ui.demos.viewinterop.ComplexTouchInterop
+import androidx.compose.ui.demos.viewinterop.ComposeInAndroidCoordinatorLayout
 import androidx.compose.ui.demos.viewinterop.ComposeInAndroidDemos
+import androidx.compose.ui.demos.viewinterop.ComposeViewComposeNestedInterop
 import androidx.compose.ui.demos.viewinterop.EditTextInteropDemo
 import androidx.compose.ui.demos.viewinterop.FocusTransferDemo
+import androidx.compose.ui.demos.viewinterop.NestedScrollInteropComposeParentWithAndroidChild
+import androidx.compose.ui.demos.viewinterop.ViewComposeViewNestedScrollInteropDemo
 import androidx.compose.ui.demos.viewinterop.ViewInteropDemo
 import androidx.compose.ui.samples.NestedScrollConnectionSample
-import androidx.compose.ui.demos.recyclerview.RecyclerViewDemos
 
 private val GestureDemos = DemoCategory(
     "Gestures",
@@ -156,6 +162,27 @@
     )
 )
 
+@OptIn(ExperimentalComposeUiApi::class)
+private val NestedScrollInteropDemos = DemoCategory(
+    "Nested Scroll Interop",
+    listOf(
+        ActivityDemo(
+            "(Collaborating) View -> Compose",
+            ComposeInAndroidCoordinatorLayout::class
+        ),
+        ActivityDemo(
+            "(Collaborating) View -> Compose -> View",
+            ViewComposeViewNestedScrollInteropDemo::class
+        ),
+        ComposableDemo("Compose -> View") {
+            NestedScrollInteropComposeParentWithAndroidChild()
+        },
+        ComposableDemo("Compose -> (Collaborating) View -> Compose Interop") {
+            ComposeViewComposeNestedInterop()
+        }
+    )
+)
+
 private val ViewInteropDemos = DemoCategory(
     "View Interop",
     listOf(
@@ -165,6 +192,7 @@
         ComplexTouchInterop,
         ComposableDemo("TextField Interop") { EditTextInteropDemo() },
         ComposableDemo("Focus Transfer") { FocusTransferDemo() },
+        NestedScrollInteropDemos
     )
 )
 
diff --git a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/viewinterop/NestedScrollInterop.kt b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/viewinterop/NestedScrollInterop.kt
new file mode 100644
index 0000000..51180eb
--- /dev/null
+++ b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/viewinterop/NestedScrollInterop.kt
@@ -0,0 +1,222 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.demos.viewinterop
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.TextView
+import androidx.activity.ComponentActivity
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.offset
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.material.Text
+import androidx.compose.material.TopAppBar
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.demos.R
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
+import androidx.compose.ui.input.nestedscroll.NestedScrollSource
+import androidx.compose.ui.input.nestedscroll.nestedScroll
+import androidx.compose.ui.platform.ComposeView
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.platform.rememberNestedScrollInteropConnection
+import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.viewinterop.AndroidView
+import androidx.core.view.ViewCompat
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import kotlin.math.roundToInt
+
+private val ToolbarHeight = 48.dp
+
+@SuppressLint("UnnecessaryLambdaCreation")
+@Composable
+private fun OuterComposeWithNestedScroll(factory: (Context) -> View) {
+    val toolbarHeightPx = with(LocalDensity.current) { ToolbarHeight.roundToPx().toFloat() }
+    val toolbarOffsetHeightPx = remember { mutableStateOf(0f) }
+
+    val nestedScrollConnection = remember {
+        object : NestedScrollConnection {
+            override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
+                val delta = available.y
+                val newOffset = toolbarOffsetHeightPx.value + delta
+                toolbarOffsetHeightPx.value = newOffset.coerceIn(-toolbarHeightPx, 0f)
+                return Offset.Zero
+            }
+        }
+    }
+
+    // Compose Scrollable
+    Box(
+        Modifier
+            .fillMaxSize()
+            .nestedScroll(nestedScrollConnection)
+    ) {
+
+        TopAppBar(
+            modifier = Modifier
+                .height(ToolbarHeight)
+                .offset { IntOffset(x = 0, y = toolbarOffsetHeightPx.value.roundToInt()) },
+            title = { Text("toolbar offset is ${toolbarOffsetHeightPx.value}") }
+        )
+
+        // Android View
+        AndroidView(
+            factory = { context -> factory(context) },
+            modifier = Modifier.fillMaxWidth()
+        )
+    }
+}
+
+private fun AndroidViewWithNestedScrollEnabled(context: Context): View {
+    return LayoutInflater.from(context)
+        .inflate(R.layout.android_in_compose_nested_scroll_interop, null).apply {
+            with(findViewById<RecyclerView>(R.id.main_list)) {
+                layoutManager =
+                    LinearLayoutManager(context, RecyclerView.VERTICAL, false)
+                adapter = NestedScrollInteropAdapter()
+            }
+        }.also {
+            ViewCompat.setNestedScrollingEnabled(it, true)
+        }
+}
+
+@Composable
+internal fun NestedScrollInteropComposeParentWithAndroidChild() {
+    OuterComposeWithNestedScroll { context ->
+        AndroidViewWithNestedScrollEnabled(context)
+    }
+}
+
+@OptIn(ExperimentalComposeUiApi::class)
+@Composable
+private fun LazyColumnWithNestedScrollInteropEnabled() {
+    LazyColumn(
+        modifier = Modifier.nestedScroll(
+            rememberNestedScrollInteropConnection()
+        ),
+        contentPadding = PaddingValues(top = ToolbarHeight)
+    ) {
+        item {
+            Text("This is a Lazy Column")
+        }
+        items(40) { item ->
+            Box(
+                modifier = Modifier
+                    .padding(16.dp)
+                    .height(56.dp)
+                    .fillMaxWidth()
+                    .background(Color.Gray),
+                contentAlignment = Alignment.Center
+            ) {
+                Text(item.toString())
+            }
+        }
+    }
+}
+
+@Composable
+internal fun ComposeViewComposeNestedInterop() {
+    OuterComposeWithNestedScroll { context ->
+        LayoutInflater.from(context)
+            .inflate(R.layout.three_fold_nested_scroll_interop, null).apply {
+                with(findViewById<ComposeView>(R.id.compose_view)) {
+                    setContent { LazyColumnWithNestedScrollInteropEnabled() }
+                }
+            }.also {
+                ViewCompat.setNestedScrollingEnabled(it, true)
+            }
+    }
+}
+
+private class NestedScrollInteropAdapter :
+    RecyclerView.Adapter<NestedScrollInteropAdapter.NestedScrollInteropViewHolder>() {
+    val items = (1..100).map { it.toString() }
+    override fun onCreateViewHolder(
+        parent: ViewGroup,
+        viewType: Int
+    ): NestedScrollInteropViewHolder {
+        return NestedScrollInteropViewHolder(
+            LayoutInflater.from(parent.context)
+                .inflate(R.layout.android_in_compose_nested_scroll_interop_list_item, parent, false)
+        )
+    }
+
+    override fun onBindViewHolder(holder: NestedScrollInteropViewHolder, position: Int) {
+        if (position == 0) {
+            holder.bind("This is a RV")
+        } else {
+            holder.bind(items[position])
+        }
+    }
+
+    override fun getItemCount(): Int = items.size
+
+    class NestedScrollInteropViewHolder(view: View) : RecyclerView.ViewHolder(view) {
+        fun bind(item: String) {
+            itemView.findViewById<TextView>(R.id.list_item).text = item
+        }
+    }
+}
+
+@ExperimentalComposeUiApi
+internal class ComposeInAndroidCoordinatorLayout : ComponentActivity() {
+    @SuppressLint("SetTextI18n")
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setContentView(R.layout.compose_in_android_coordinator_layout)
+        findViewById<ComposeView>(R.id.compose_view).apply {
+            setContent { LazyColumnWithNestedScrollInteropEnabled() }
+        }
+    }
+}
+
+@ExperimentalComposeUiApi
+internal class ViewComposeViewNestedScrollInteropDemo : ComponentActivity() {
+    @SuppressLint("SetTextI18n")
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setContentView(R.layout.compose_in_android_coordinator_layout)
+        findViewById<ComposeView>(R.id.compose_view).apply {
+            setContent {
+                val nestedScrollInterop = rememberNestedScrollInteropConnection()
+                Box(modifier = Modifier.nestedScroll(nestedScrollInterop)) {
+                    AndroidView({ context ->
+                        AndroidViewWithNestedScrollEnabled(context)
+                    }, modifier = Modifier.fillMaxWidth())
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/viewinterop/PointerInputInteropAndroidInCompose.kt b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/viewinterop/PointerInputInteropAndroidInCompose.kt
index 5c85842..595b15f 100644
--- a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/viewinterop/PointerInputInteropAndroidInCompose.kt
+++ b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/viewinterop/PointerInputInteropAndroidInCompose.kt
@@ -20,7 +20,6 @@
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
-import android.widget.TextView
 import androidx.compose.foundation.background
 import androidx.compose.foundation.gestures.detectTapGestures
 import androidx.compose.foundation.horizontalScroll
@@ -28,9 +27,7 @@
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.offset
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.layout.wrapContentSize
@@ -39,7 +36,6 @@
 import androidx.compose.integration.demos.common.ComposableDemo
 import androidx.compose.integration.demos.common.DemoCategory
 import androidx.compose.material.Text
-import androidx.compose.material.TopAppBar
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.MutableState
 import androidx.compose.runtime.mutableStateOf
@@ -49,22 +45,10 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.demos.R
 import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
-import androidx.compose.ui.input.nestedscroll.NestedScrollSource
-import androidx.compose.ui.input.nestedscroll.nestedScroll
 import androidx.compose.ui.input.pointer.pointerInput
 import androidx.compose.ui.input.pointer.pointerInteropFilter
-import androidx.compose.ui.platform.LocalDensity
-import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.viewinterop.AndroidView
-import androidx.core.view.ViewCompat
-import androidx.recyclerview.widget.LinearLayoutManager
-import androidx.recyclerview.widget.RecyclerView
-import androidx.recyclerview.widget.RecyclerView.Adapter
-import androidx.recyclerview.widget.RecyclerView.VERTICAL
-import androidx.recyclerview.widget.RecyclerView.ViewHolder
-import kotlin.math.roundToInt
 
 @OptIn(ExperimentalComposeUiApi::class)
 val AndroidInComposeDemos = DemoCategory(
@@ -85,9 +69,6 @@
         ComposableDemo("MotionEventPointerInputFilter") {
             PointerInteropFilterDemo()
         },
-        ComposableDemo("Nested Scroll Interop Compose Parent With Android Child") {
-            NestedScrollInteropComposeParentWithAndroidChild()
-        }
     )
 )
 
@@ -335,79 +316,4 @@
             Text(motionEventString.value)
         }
     }
-}
-
-val ToolbarHeight = 48.dp
-
-@Composable
-private fun NestedScrollInteropComposeParentWithAndroidChild() {
-    val toolbarHeightPx = with(LocalDensity.current) { ToolbarHeight.roundToPx().toFloat() }
-    val toolbarOffsetHeightPx = remember { mutableStateOf(0f) }
-
-    val nestedScrollConnection = remember {
-        object : NestedScrollConnection {
-            override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
-
-                val delta = available.y
-                val newOffset = toolbarOffsetHeightPx.value + delta
-                toolbarOffsetHeightPx.value = newOffset.coerceIn(-toolbarHeightPx, 0f)
-                return Offset.Zero
-            }
-        }
-    }
-
-    Box(
-        Modifier
-            .fillMaxSize()
-            .nestedScroll(nestedScrollConnection)
-    ) {
-
-        TopAppBar(
-            modifier = Modifier
-                .height(ToolbarHeight)
-                .offset { IntOffset(x = 0, y = toolbarOffsetHeightPx.value.roundToInt()) },
-            title = { Text("toolbar offset is ${toolbarOffsetHeightPx.value}") }
-        )
-
-        AndroidView(
-            { context ->
-                LayoutInflater.from(context)
-                    .inflate(R.layout.android_in_compose_nested_scroll_interop, null).apply {
-                        with(findViewById<RecyclerView>(R.id.main_list)) {
-                            layoutManager = LinearLayoutManager(context, VERTICAL, false)
-                            adapter = NestedScrollInteropAdapter()
-                        }
-                    }.also {
-                        ViewCompat.setNestedScrollingEnabled(it, true)
-                    }
-            },
-            modifier = Modifier.padding(top = ToolbarHeight).fillMaxWidth(),
-        )
-    }
-}
-
-private class NestedScrollInteropAdapter :
-    Adapter<NestedScrollInteropAdapter.NestedScrollInteropViewHolder>() {
-    val items = (1..100).map { it.toString() }
-    override fun onCreateViewHolder(
-        parent: ViewGroup,
-        viewType: Int
-    ): NestedScrollInteropViewHolder {
-        return NestedScrollInteropViewHolder(
-            LayoutInflater.from(parent.context)
-                .inflate(R.layout.android_in_compose_nested_scroll_interop_list_item, parent, false)
-        )
-    }
-
-    override fun onBindViewHolder(holder: NestedScrollInteropViewHolder, position: Int) {
-        holder.bind(items[position])
-    }
-
-    override fun getItemCount(): Int = items.size
-
-    class NestedScrollInteropViewHolder(view: View) : ViewHolder(view) {
-        fun bind(item: String) {
-            itemView.findViewById<TextView>(R.id.list_item).text = item
-        }
-    }
 }
\ No newline at end of file
diff --git a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/viewinterop/PointerInputInteropComposeInAndroid.kt b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/viewinterop/PointerInputInteropComposeInAndroid.kt
index 8517967c..e071074 100644
--- a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/viewinterop/PointerInputInteropComposeInAndroid.kt
+++ b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/viewinterop/PointerInputInteropComposeInAndroid.kt
@@ -43,7 +43,6 @@
 import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.layout.width
 import androidx.compose.foundation.layout.wrapContentSize
-import androidx.compose.foundation.lazy.LazyColumn
 import androidx.compose.foundation.lazy.LazyRow
 import androidx.compose.foundation.rememberScrollState
 import androidx.compose.foundation.verticalScroll
@@ -55,17 +54,14 @@
 import androidx.compose.material.Text
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
-import androidx.compose.ui.Alignment
 import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.demos.R
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.RectangleShape
 import androidx.compose.ui.graphics.toArgb
-import androidx.compose.ui.input.nestedscroll.nestedScroll
 import androidx.compose.ui.input.pointer.pointerInput
 import androidx.compose.ui.platform.ComposeView
-import androidx.compose.ui.platform.rememberNestedScrollInteropConnection
 import androidx.compose.ui.unit.dp
 import androidx.fragment.app.DialogFragment
 import androidx.fragment.app.FragmentActivity
@@ -103,10 +99,6 @@
         ActivityDemo(
             "Compose in Android dialog dismisses dialog during dispatch",
             ComposeInAndroidDialogDismissDialogDuringDispatch::class
-        ),
-        ActivityDemo(
-            "Compose scroll in Android Coordinator Layout",
-            ComposeInAndroidCoordinatorLayout::class
         )
     )
 )
@@ -419,34 +411,6 @@
     }
 }
 
-@ExperimentalComposeUiApi
-open class ComposeInAndroidCoordinatorLayout : ComponentActivity() {
-    @SuppressLint("SetTextI18n")
-    override fun onCreate(savedInstanceState: Bundle?) {
-        super.onCreate(savedInstanceState)
-        setContentView(R.layout.compose_in_android_coordinator_layout)
-        findViewById<ComposeView>(R.id.compose_view).apply {
-            setContent {
-                val nestedScrollInterop = rememberNestedScrollInteropConnection(this)
-                LazyColumn(modifier = Modifier.nestedScroll(nestedScrollInterop)) {
-                    items(20) { item ->
-                        Box(
-                            modifier = Modifier
-                                .padding(16.dp)
-                                .height(56.dp)
-                                .fillMaxWidth()
-                                .background(Color.Gray),
-                            contentAlignment = Alignment.Center
-                        ) {
-                            Text(item.toString())
-                        }
-                    }
-                }
-            }
-        }
-    }
-}
-
 class MyDialogFragment : DialogFragment() {
 
     override fun onCreateView(
diff --git a/compose/ui/ui/integration-tests/ui-demos/src/main/res/layout/android_in_compose_nested_scroll_interop.xml b/compose/ui/ui/integration-tests/ui-demos/src/main/res/layout/android_in_compose_nested_scroll_interop.xml
index f0342bf..a99fe70 100644
--- a/compose/ui/ui/integration-tests/ui-demos/src/main/res/layout/android_in_compose_nested_scroll_interop.xml
+++ b/compose/ui/ui/integration-tests/ui-demos/src/main/res/layout/android_in_compose_nested_scroll_interop.xml
@@ -21,5 +21,6 @@
     <androidx.recyclerview.widget.RecyclerView
         android:id="@+id/main_list"
         android:layout_width="match_parent"
-        android:layout_height="match_parent" />
+        android:layout_height="match_parent"
+        android:layout_marginTop="48dp" />
 </FrameLayout>
\ No newline at end of file
diff --git a/compose/ui/ui/integration-tests/ui-demos/src/main/res/layout/three_fold_nested_scroll_interop.xml b/compose/ui/ui/integration-tests/ui-demos/src/main/res/layout/three_fold_nested_scroll_interop.xml
new file mode 100644
index 0000000..e07883f
--- /dev/null
+++ b/compose/ui/ui/integration-tests/ui-demos/src/main/res/layout/three_fold_nested_scroll_interop.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  Copyright 2022 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<androidx.coordinatorlayout.widget.CoordinatorLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <androidx.compose.ui.platform.ComposeView
+        android:id="@+id/compose_view"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content" />
+</androidx.coordinatorlayout.widget.CoordinatorLayout>
\ No newline at end of file
diff --git a/compose/ui/ui/samples/build.gradle b/compose/ui/ui/samples/build.gradle
index 69b7d18..4f202cc 100644
--- a/compose/ui/ui/samples/build.gradle
+++ b/compose/ui/ui/samples/build.gradle
@@ -27,6 +27,7 @@
     kotlinPlugin(project(":compose:compiler:compiler"))
 
     implementation(libs.kotlinStdlib)
+    implementation(libs.material)
     implementation("androidx.core:core:1.5.0")
 
     compileOnly(project(":annotation:annotation-sampled"))
diff --git a/compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/NestedScrollInteropSamples.kt b/compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/NestedScrollInteropSamples.kt
index 9ecba71..5bca702 100644
--- a/compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/NestedScrollInteropSamples.kt
+++ b/compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/NestedScrollInteropSamples.kt
@@ -16,51 +16,62 @@
 
 package androidx.compose.ui.samples
 
+import android.content.Context
 import android.view.LayoutInflater
+import android.view.View
 import androidx.annotation.Sampled
 import androidx.compose.foundation.background
 import androidx.compose.foundation.gestures.Orientation
 import androidx.compose.foundation.gestures.rememberScrollableState
 import androidx.compose.foundation.gestures.scrollable
 import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.offset
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.lazy.LazyColumn
 import androidx.compose.material.Text
+import androidx.compose.material.TopAppBar
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
+import androidx.compose.ui.input.nestedscroll.NestedScrollSource
 import androidx.compose.ui.input.nestedscroll.nestedScroll
 import androidx.compose.ui.platform.ComposeView
+import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.platform.rememberNestedScrollInteropConnection
+import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.viewinterop.AndroidView
 import androidx.core.view.ViewCompat
+import kotlin.math.roundToInt
 
 @OptIn(ExperimentalComposeUiApi::class)
 @Sampled
 @Composable
-fun ComposeInCooperatingViewNestedScrollInteropSample(composeView: ComposeView) {
-    with(composeView) {
-        val nestedSrollInterop = rememberNestedScrollInteropConnection(this)
-        // Add the nested scroll connection to your top level @Composable element
-        // using the nestedScroll modifier.
-        LazyColumn(modifier = Modifier.nestedScroll(nestedSrollInterop)) {
-            items(20) { item ->
-                Box(
-                    modifier = Modifier
-                        .padding(16.dp)
-                        .height(56.dp)
-                        .fillMaxWidth()
-                        .background(Color.Gray),
-                    contentAlignment = Alignment.Center
-                ) {
-                    Text(item.toString())
-                }
+fun ComposeInCooperatingViewNestedScrollInteropSample() {
+    val nestedSrollInterop = rememberNestedScrollInteropConnection()
+    // Add the nested scroll connection to your top level @Composable element
+    // using the nestedScroll modifier.
+    LazyColumn(modifier = Modifier.nestedScroll(nestedSrollInterop)) {
+        items(20) { item ->
+            Box(
+                modifier = Modifier
+                    .padding(16.dp)
+                    .height(56.dp)
+                    .fillMaxWidth()
+                    .background(Color.Gray),
+                contentAlignment = Alignment.Center
+            ) {
+                Text(item.toString())
             }
         }
     }
@@ -90,4 +101,81 @@
             }
         )
     }
-}
\ No newline at end of file
+}
+
+private val ToolbarHeight = 48.dp
+
+@Composable
+fun CollapsingToolbarComposeViewComposeNestedScrollInteropSample() {
+    val toolbarHeightPx = with(LocalDensity.current) { ToolbarHeight.roundToPx().toFloat() }
+    val toolbarOffsetHeightPx = remember { mutableStateOf(0f) }
+
+    val nestedScrollConnection = remember {
+        object : NestedScrollConnection {
+            override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
+                val delta = available.y
+                val newOffset = toolbarOffsetHeightPx.value + delta
+                toolbarOffsetHeightPx.value = newOffset.coerceIn(-toolbarHeightPx, 0f)
+                return Offset.Zero
+            }
+        }
+    }
+
+    // Compose Scrollable
+    Box(
+        Modifier
+            .fillMaxSize()
+            .nestedScroll(nestedScrollConnection)
+    ) {
+        TopAppBar(
+            modifier = Modifier
+                .height(ToolbarHeight)
+                .offset { IntOffset(x = 0, y = toolbarOffsetHeightPx.value.roundToInt()) },
+            title = { Text("toolbar offset is ${toolbarOffsetHeightPx.value}") }
+        )
+        // Android View
+        AndroidView(
+            factory = { context -> AndroidViewWithCompose(context) },
+            modifier = Modifier.fillMaxWidth()
+        )
+    }
+}
+
+private fun AndroidViewWithCompose(context: Context): View {
+    return LayoutInflater.from(context)
+        .inflate(R.layout.three_fold_nested_scroll_interop, null).apply {
+            with(findViewById<ComposeView>(R.id.compose_view)) {
+                // Compose
+                setContent { LazyColumnWithNestedScrollInteropEnabled() }
+            }
+        }.also {
+            ViewCompat.setNestedScrollingEnabled(it, true)
+        }
+}
+
+@OptIn(ExperimentalComposeUiApi::class)
+@Composable
+private fun LazyColumnWithNestedScrollInteropEnabled() {
+    LazyColumn(
+        modifier = Modifier.nestedScroll(
+            rememberNestedScrollInteropConnection()
+        ),
+        contentPadding = PaddingValues(top = ToolbarHeight)
+    ) {
+        item {
+            Text("This is a Lazy Column")
+        }
+        items(40) { item ->
+            Box(
+                modifier = Modifier
+                    .padding(16.dp)
+                    .height(56.dp)
+                    .fillMaxWidth()
+                    .background(Color.Gray),
+                contentAlignment = Alignment.Center
+            ) {
+                Text(item.toString())
+            }
+        }
+    }
+}
diff --git a/compose/ui/ui/samples/src/main/res/layout/three_fold_nested_scroll_interop.xml b/compose/ui/ui/samples/src/main/res/layout/three_fold_nested_scroll_interop.xml
new file mode 100644
index 0000000..e07883f
--- /dev/null
+++ b/compose/ui/ui/samples/src/main/res/layout/three_fold_nested_scroll_interop.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  Copyright 2022 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<androidx.coordinatorlayout.widget.CoordinatorLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <androidx.compose.ui.platform.ComposeView
+        android:id="@+id/compose_view"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content" />
+</androidx.coordinatorlayout.widget.CoordinatorLayout>
\ No newline at end of file
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/viewinterop/NestedScrollInteropConnectionTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/viewinterop/NestedScrollInteropConnectionTest.kt
index cc644cb..73c9f5e 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/viewinterop/NestedScrollInteropConnectionTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/viewinterop/NestedScrollInteropConnectionTest.kt
@@ -16,11 +16,7 @@
 
 package androidx.compose.ui.viewinterop
 
-import android.content.Context
-import android.util.AttributeSet
-import android.view.View
 import androidx.activity.ComponentActivity
-import androidx.annotation.LayoutRes
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.height
@@ -34,12 +30,8 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.input.nestedscroll.ModifierLocalNestedScroll
-import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
-import androidx.compose.ui.input.nestedscroll.NestedScrollSource
 import androidx.compose.ui.input.nestedscroll.nestedScroll
 import androidx.compose.ui.modifier.modifierLocalProvider
-import androidx.compose.ui.platform.ComposeView
-import androidx.compose.ui.platform.rememberNestedScrollInteropConnection
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.test.R
 import androidx.compose.ui.test.assertIsDisplayed
@@ -49,12 +41,8 @@
 import androidx.compose.ui.test.swipeDown
 import androidx.compose.ui.test.swipeUp
 import androidx.compose.ui.test.swipeWithVelocity
-import androidx.compose.ui.unit.Velocity
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.round
-import androidx.coordinatorlayout.widget.CoordinatorLayout
-import androidx.lifecycle.Lifecycle
-import androidx.test.core.app.ActivityScenario
 import androidx.test.espresso.Espresso.onView
 import androidx.test.espresso.action.ViewActions.click
 import androidx.test.espresso.action.ViewActions.swipeUp
@@ -66,6 +54,7 @@
 import com.google.common.truth.Truth.assertThat
 import kotlin.math.abs
 import org.hamcrest.Matchers.not
+import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -76,6 +65,7 @@
 
     @get:Rule
     val rule = createAndroidComposeRule<ComponentActivity>()
+    private val deltaCollectorNestedScrollConnection = InspectableNestedScrollConnection()
 
     private val nestedScrollParentView by lazy {
         rule.activity.findViewById<TestNestedScrollParentView>(R.id.main_layout)
@@ -88,6 +78,11 @@
     private val appBarScrollDelta = appBarExpandedSize - appBarCollapsedSize
     private val completelyCollapsedScroll = 600.dp
 
+    @Before
+    fun setUp() {
+        deltaCollectorNestedScrollConnection.reset()
+    }
+
     @Test
     fun swipeComposeScrollable_insideNestedScrollingParentView_shouldScrollViewToo() {
         // arrange
@@ -128,11 +123,11 @@
             assertThat(
                 abs(
                     nestedScrollParentView.offeredToParentOffset.y -
-                        deltaCollectorNestedScrollConnection.availableToParent.y
+                        deltaCollectorNestedScrollConnection.offeredFromChild.y
                 )
             ).isAtMost(ScrollRoundingErrorTolerance)
 
-            assertThat(deltaCollectorNestedScrollConnection.consumedByChildren)
+            assertThat(deltaCollectorNestedScrollConnection.consumedDownChain)
                 .isEqualTo(Offset.Zero)
         }
     }
@@ -247,7 +242,7 @@
         rule.runOnIdle {
             val appBarScrollDeltaPixels = appBarScrollDelta.value * rule.density.density * -1
             val offeredToParent = nestedScrollParentView.offeredToParentOffset.y
-            val availableToParent = deltaCollectorNestedScrollConnection.availableToParent.y +
+            val availableToParent = deltaCollectorNestedScrollConnection.offeredFromChild.y +
                 appBarScrollDeltaPixels
             assertThat(offeredToParent - availableToParent).isAtMost(ScrollRoundingErrorTolerance)
         }
@@ -274,7 +269,7 @@
 
         // assert: check that whatever that is unconsumed by view was consumed by children
         val unconsumedInView = nestedScrollParentView.unconsumedOffset.round().y
-        val consumedByChildren = deltaCollectorNestedScrollConnection.consumedByChildren.round().y
+        val consumedByChildren = deltaCollectorNestedScrollConnection.consumedDownChain.round().y
         rule.runOnIdle {
             assertThat(abs(unconsumedInView - consumedByChildren))
                 .isAtMost(ScrollRoundingErrorTolerance)
@@ -303,7 +298,7 @@
         // assert: check that whatever that is unconsumed by view was consumed by children
         val velocityOfferedInView = abs(nestedScrollParentView.velocityOfferedToParentOffset.y)
         val velocityAvailableInCompose =
-            abs(deltaCollectorNestedScrollConnection.velocityAvailableToParent.y)
+            abs(deltaCollectorNestedScrollConnection.velocityOfferedFromChild.y)
         rule.runOnIdle {
             assertThat(velocityOfferedInView - velocityAvailableInCompose).isEqualTo(
                 VelocityRoundingErrorTolerance
@@ -333,7 +328,7 @@
         // assert: check that whatever that is unconsumed by view was consumed by children
         val velocityUnconsumedOffset = abs(nestedScrollParentView.velocityUnconsumedOffset.y)
         val velocityConsumedByChildren =
-            abs(deltaCollectorNestedScrollConnection.velocityConsumedByChildren.y)
+            abs(deltaCollectorNestedScrollConnection.velocityConsumedDownChain.y)
         rule.runOnIdle {
             assertThat(abs(velocityUnconsumedOffset - velocityConsumedByChildren)).isAtMost(
                 VelocityRoundingErrorTolerance
@@ -350,11 +345,10 @@
             .activityRule
             .scenario
             .createActivityWithComposeContent(
-                R.layout.test_nested_scroll_coordinator_layout,
-                enableInterop,
-                content
+                layout = R.layout.test_nested_scroll_coordinator_layout,
+                enableInterop = enableInterop,
+                content = content
             )
-        deltaCollectorNestedScrollConnection.onNestedScrollStarted()
     }
 }
 
@@ -374,153 +368,12 @@
 @Composable
 private fun TestItem(item: String) {
     Box(
-        modifier = Modifier.padding(16.dp).height(56.dp).fillMaxWidth()
+        modifier = Modifier
+            .padding(16.dp)
+            .height(56.dp)
+            .fillMaxWidth()
             .testTag(item), contentAlignment = Alignment.Center
     ) {
         BasicText(item)
     }
 }
-
-@ExperimentalComposeUiApi
-private fun ActivityScenario<*>.createActivityWithComposeContent(
-    @LayoutRes layout: Int,
-    enableInterop: Boolean,
-    content: @Composable () -> Unit
-) {
-    onActivity { activity ->
-        activity.setTheme(R.style.Theme_MaterialComponents_Light)
-        activity.setContentView(layout)
-        with(activity.findViewById<ComposeView>(R.id.compose_view)) {
-            setContent {
-                val nestedScrollInterop = if (enableInterop) Modifier.nestedScroll(
-                    rememberNestedScrollInteropConnection(this)
-                ) else Modifier
-                Box(nestedScrollInterop) {
-                    content()
-                }
-            }
-        }
-    }
-    moveToState(Lifecycle.State.RESUMED)
-}
-
-internal class TestNestedScrollParentView(
-    context: Context,
-    attrs: AttributeSet
-) : CoordinatorLayout(context, attrs) {
-
-    private val unconsumed = IntArray(2)
-    val unconsumedOffset: Offset
-        get() = unconsumed.toReversedOffset()
-
-    private val offeredToParent = IntArray(2)
-    val offeredToParentOffset: Offset
-        get() = offeredToParent.toReversedOffset()
-
-    private val velocityOfferedToParent = FloatArray(2)
-    val velocityOfferedToParentOffset: Velocity
-        get() = velocityOfferedToParent.toReversedVelocity()
-
-    private val velocityUnconsumed = FloatArray(2)
-    val velocityUnconsumedOffset: Velocity
-        get() = velocityUnconsumed.toReversedVelocity()
-
-    override fun onNestedPreScroll(target: View, dx: Int, dy: Int, consumed: IntArray, type: Int) {
-        super.onNestedPreScroll(target, dx, dy, consumed, type)
-        offeredToParent[0] += dx
-        offeredToParent[1] += dy
-    }
-
-    override fun onStartNestedScroll(child: View, target: View, axes: Int, type: Int): Boolean {
-        unconsumed.fill(0)
-        offeredToParent.fill(0)
-        return super.onStartNestedScroll(child, target, axes, type)
-    }
-
-    override fun onNestedScroll(
-        target: View,
-        dxConsumed: Int,
-        dyConsumed: Int,
-        dxUnconsumed: Int,
-        dyUnconsumed: Int,
-        type: Int,
-        consumed: IntArray
-    ) {
-        super.onNestedScroll(
-            target,
-            dxConsumed,
-            dyConsumed,
-            dxUnconsumed,
-            dyUnconsumed,
-            type,
-            consumed
-        )
-        unconsumed[0] += dxConsumed
-        unconsumed[1] += dyConsumed
-    }
-
-    override fun onNestedPreFling(target: View, velocityX: Float, velocityY: Float): Boolean {
-        velocityOfferedToParent[0] += velocityX
-        velocityOfferedToParent[1] += velocityY
-        return super.onNestedPreFling(target, velocityX, velocityY)
-    }
-
-    override fun onNestedFling(
-        target: View,
-        velocityX: Float,
-        velocityY: Float,
-        consumed: Boolean
-    ): Boolean {
-        velocityUnconsumed[0] += velocityX
-        velocityUnconsumed[0] += velocityY
-        return super.onNestedFling(target, velocityX, velocityY, consumed)
-    }
-}
-
-private fun IntArray.toReversedOffset(): Offset {
-    require(size == 2)
-    return Offset(this[0] * -1f, this[1] * -1f)
-}
-
-private fun FloatArray.toReversedVelocity(): Velocity {
-    require(size == 2)
-    return Velocity(this[0] * -1f, this[1] * -1f)
-}
-
-private val deltaCollectorNestedScrollConnection = object : NestedScrollConnection {
-    var consumedByChildren = Offset(0f, 0f)
-    var velocityConsumedByChildren = Velocity(0f, 0f)
-    var availableToParent = Offset(0f, 0f)
-    var velocityAvailableToParent = Velocity(0f, 0f)
-
-    override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
-        availableToParent += available
-        return Offset.Zero
-    }
-
-    override fun onPostScroll(
-        consumed: Offset,
-        available: Offset,
-        source: NestedScrollSource
-    ): Offset {
-        consumedByChildren += consumed
-        return Offset.Zero
-    }
-
-    override suspend fun onPreFling(available: Velocity): Velocity {
-        velocityAvailableToParent += available
-        return Velocity.Zero
-    }
-
-    override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity {
-        velocityConsumedByChildren += consumed
-        return Velocity.Zero
-    }
-
-    fun onNestedScrollStarted() {
-        consumedByChildren = Offset(0f, 0f)
-        availableToParent = Offset(0f, 0f)
-        velocityConsumedByChildren = Velocity(0f, 0f)
-        velocityAvailableToParent = Velocity(0f, 0f)
-    }
-}
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/viewinterop/NestedScrollInteropTestHelper.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/viewinterop/NestedScrollInteropTestHelper.kt
new file mode 100644
index 0000000..8d31975
--- /dev/null
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/viewinterop/NestedScrollInteropTestHelper.kt
@@ -0,0 +1,417 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.viewinterop
+
+import android.content.Context
+import android.os.Build
+import android.util.AttributeSet
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.TextView
+import androidx.annotation.LayoutRes
+import androidx.annotation.RequiresApi
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
+import androidx.compose.ui.input.nestedscroll.NestedScrollSource
+import androidx.compose.ui.input.nestedscroll.nestedScroll
+import androidx.compose.ui.platform.ComposeView
+import androidx.compose.ui.platform.rememberNestedScrollInteropConnection
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.R
+import androidx.compose.ui.unit.Velocity
+import androidx.compose.ui.unit.dp
+import androidx.coordinatorlayout.widget.CoordinatorLayout
+import androidx.core.view.ViewCompat
+import androidx.lifecycle.Lifecycle
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import androidx.test.core.app.ActivityScenario
+
+internal const val MainTestList = "mainList"
+internal const val OuterBoxLayout = "outerBoxLayout"
+internal const val AndroidViewContainer = "androidView"
+
+internal class NestedScrollInteropAdapter :
+    RecyclerView.Adapter<NestedScrollInteropAdapter.SimpleTextViewHolder>() {
+    val items = (1..200).map { it.toString() }
+    override fun onCreateViewHolder(
+        parent: ViewGroup,
+        viewType: Int
+    ): SimpleTextViewHolder {
+        return SimpleTextViewHolder(
+            LayoutInflater.from(parent.context)
+                .inflate(
+                    R.layout.android_in_compose_nested_scroll_interop_list_item,
+                    parent,
+                    false
+                )
+        )
+    }
+
+    override fun onBindViewHolder(holder: SimpleTextViewHolder, position: Int) {
+        holder.bind(items[position])
+    }
+
+    override fun getItemCount(): Int = items.size
+
+    class SimpleTextViewHolder(view: View) : RecyclerView.ViewHolder(view) {
+        fun bind(item: String) {
+            itemView.findViewById<TextView>(R.id.list_item).text = item
+        }
+    }
+}
+
+internal open class InspectableNestedScrollConnection() : NestedScrollConnection {
+    var offeredFromChild = Offset.Zero
+    var velocityOfferedFromChild = Velocity.Zero
+    var consumedDownChain = Offset.Zero
+    var velocityConsumedDownChain = Velocity.Zero
+    var notConsumedByChild = Offset.Zero
+    var velocityNotConsumedByChild = Velocity.Zero
+
+    override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
+        offeredFromChild += available
+        return Offset.Zero
+    }
+
+    override fun onPostScroll(
+        consumed: Offset,
+        available: Offset,
+        source: NestedScrollSource
+    ): Offset {
+        consumedDownChain += consumed
+        notConsumedByChild += available
+        return Offset.Zero
+    }
+
+    override suspend fun onPreFling(available: Velocity): Velocity {
+        velocityOfferedFromChild += available
+        return Velocity.Zero
+    }
+
+    override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity {
+        velocityConsumedDownChain += consumed
+        velocityNotConsumedByChild += available
+        return Velocity.Zero
+    }
+
+    fun reset() {
+        offeredFromChild = Offset.Zero
+        velocityOfferedFromChild = Velocity.Zero
+        consumedDownChain = Offset.Zero
+        velocityConsumedDownChain = Velocity.Zero
+        notConsumedByChild = Offset.Zero
+        velocityNotConsumedByChild = Velocity.Zero
+    }
+}
+
+internal class TestNestedScrollParentView(
+    context: Context,
+    attrs: AttributeSet
+) : CoordinatorLayout(context, attrs) {
+
+    private val unconsumed = IntArray(2)
+    val unconsumedOffset: Offset
+        get() = unconsumed.toReversedOffset()
+
+    private val offeredToParent = IntArray(2)
+    val offeredToParentOffset: Offset
+        get() = offeredToParent.toReversedOffset()
+
+    private val velocityOfferedToParent = FloatArray(2)
+    val velocityOfferedToParentOffset: Velocity
+        get() = velocityOfferedToParent.toReversedVelocity()
+
+    private val velocityUnconsumed = FloatArray(2)
+    val velocityUnconsumedOffset: Velocity
+        get() = velocityUnconsumed.toReversedVelocity()
+
+    override fun onNestedPreScroll(target: View, dx: Int, dy: Int, consumed: IntArray, type: Int) {
+        super.onNestedPreScroll(target, dx, dy, consumed, type)
+        offeredToParent[0] += dx
+        offeredToParent[1] += dy
+    }
+
+    override fun onStartNestedScroll(child: View, target: View, axes: Int, type: Int): Boolean {
+        unconsumed.fill(0)
+        offeredToParent.fill(0)
+        return super.onStartNestedScroll(child, target, axes, type)
+    }
+
+    override fun onNestedScroll(
+        target: View,
+        dxConsumed: Int,
+        dyConsumed: Int,
+        dxUnconsumed: Int,
+        dyUnconsumed: Int,
+        type: Int,
+        consumed: IntArray
+    ) {
+        super.onNestedScroll(
+            target,
+            dxConsumed,
+            dyConsumed,
+            dxUnconsumed,
+            dyUnconsumed,
+            type,
+            consumed
+        )
+        unconsumed[0] += dxConsumed
+        unconsumed[1] += dyConsumed
+    }
+
+    override fun onNestedPreFling(target: View, velocityX: Float, velocityY: Float): Boolean {
+        velocityOfferedToParent[0] += velocityX
+        velocityOfferedToParent[1] += velocityY
+        return super.onNestedPreFling(target, velocityX, velocityY)
+    }
+
+    override fun onNestedFling(
+        target: View,
+        velocityX: Float,
+        velocityY: Float,
+        consumed: Boolean
+    ): Boolean {
+        velocityUnconsumed[0] += velocityX
+        velocityUnconsumed[0] += velocityY
+        return super.onNestedFling(target, velocityX, velocityY, consumed)
+    }
+}
+
+internal class AllConsumingInspectableConnection : InspectableNestedScrollConnection() {
+    override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
+        super.onPreScroll(available, source)
+        return available
+    }
+
+    override fun onPostScroll(
+        consumed: Offset,
+        available: Offset,
+        source: NestedScrollSource
+    ): Offset {
+        super.onPostScroll(consumed, available, source)
+        return available
+    }
+
+    override suspend fun onPreFling(available: Velocity): Velocity {
+        super.onPreFling(available)
+        return available
+    }
+
+    override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity {
+        super.onPostFling(consumed, available)
+        return available
+    }
+}
+
+internal class RecyclerViewConsumptionTracker {
+    private var consumedByRecyclerView = intArrayOf(0, 0)
+    private var velocityConsumedByRecyclerView = intArrayOf(0, 0)
+
+    val deltaConsumed
+        get() = consumedByRecyclerView.toOffset()
+    val velocityConsumed
+        get() = velocityConsumedByRecyclerView.toComposeVelocity()
+
+    fun trackDeltaConsumption(dx: Int, dy: Int) {
+        consumedByRecyclerView[0] += dx
+        consumedByRecyclerView[1] += dy
+    }
+
+    fun trackVelocityConsumed(velocityX: Int, velocityY: Int) {
+        velocityConsumedByRecyclerView[0] += velocityX
+        velocityConsumedByRecyclerView[1] += velocityY
+    }
+
+    fun reset() {
+        consumedByRecyclerView.fill(0)
+        velocityConsumedByRecyclerView.fill(0)
+    }
+}
+
+@Composable
+internal fun NestedScrollInteropTestApp(
+    modifier: Modifier = Modifier,
+    content: (Context) -> View
+) {
+    Box(modifier.fillMaxSize().testTag(OuterBoxLayout)) {
+        AndroidView(content, modifier = Modifier.testTag(AndroidViewContainer))
+    }
+}
+
+@Composable
+internal fun NestedScrollDeepNested(
+    modifier: Modifier,
+    enabled: Boolean,
+    connection: NestedScrollConnection? = null
+) {
+    // Box (Compose) + AndroidView (View) +
+    // Box (Compose)
+    val outerModifier = if (connection == null) Modifier else Modifier.nestedScroll(connection)
+    NestedScrollInteropTestApp(modifier) { context ->
+        LayoutInflater.from(context)
+            .inflate(R.layout.test_nested_scroll_coordinator_layout_without_toolbar, null)
+            .apply {
+                with(findViewById<ComposeView>(R.id.compose_view)) {
+                    setContent {
+                        Box(modifier = outerModifier) {
+                            ComposeInViewWithNestedScrollInterop()
+                        }
+                    }
+                }
+            }.also {
+                ViewCompat.setNestedScrollingEnabled(it, enabled)
+            }
+    }
+}
+
+@OptIn(ExperimentalComposeUiApi::class)
+@Composable
+internal fun ComposeInViewWithNestedScrollInterop() {
+    LazyColumn(
+        modifier = Modifier.nestedScroll(rememberNestedScrollInteropConnection())
+            .testTag(MainTestList)
+    ) {
+        items(200) { item ->
+            Box(
+                modifier = Modifier
+                    .padding(16.dp)
+                    .height(56.dp)
+                    .fillMaxWidth()
+                    .background(Color.Gray),
+                contentAlignment = Alignment.Center
+            ) {
+                Text(item.toString())
+            }
+        }
+    }
+}
+
+@RequiresApi(Build.VERSION_CODES.M)
+@Composable
+internal fun NestedScrollInteropWithView(
+    modifier: Modifier = Modifier,
+    enabled: Boolean,
+    recyclerViewConsumptionTracker: RecyclerViewConsumptionTracker
+) {
+    NestedScrollInteropTestApp(modifier) { context ->
+        LayoutInflater.from(context)
+            .inflate(R.layout.android_in_compose_nested_scroll_interop, null)
+            .apply {
+                with(findViewById<RecyclerView>(R.id.main_list)) {
+                    layoutManager = LinearLayoutManager(
+                        context,
+                        RecyclerView.VERTICAL,
+                        false
+                    )
+                    adapter = NestedScrollInteropAdapter()
+                    setOnScrollChangeListener { _, _, _, oldX, oldY ->
+                        recyclerViewConsumptionTracker.trackDeltaConsumption(oldX, oldY)
+                    }
+                    onFlingListener = object : RecyclerView.OnFlingListener() {
+                        override fun onFling(velocityX: Int, velocityY: Int): Boolean {
+                            recyclerViewConsumptionTracker.trackVelocityConsumed(
+                                velocityX,
+                                velocityY
+                            )
+                            return false
+                        }
+                    }
+                }
+            }.also {
+                ViewCompat.setNestedScrollingEnabled(it, enabled)
+            }
+    }
+}
+
+@ExperimentalComposeUiApi
+internal fun ActivityScenario<*>.createActivityWithComposeContent(
+    @LayoutRes layout: Int,
+    enableInterop: Boolean,
+    modifier: Modifier = Modifier,
+    content: @Composable () -> Unit
+) {
+    onActivity { activity ->
+        activity.setTheme(R.style.Theme_MaterialComponents_Light)
+        activity.setContentView(layout)
+        with(activity.findViewById<ComposeView>(R.id.compose_view)) {
+            setContent {
+                val nestedScrollInterop = if (enableInterop) modifier.nestedScroll(
+                    rememberNestedScrollInteropConnection()
+                ) else modifier
+                Box(nestedScrollInterop) {
+                    content()
+                }
+            }
+        }
+    }
+    moveToState(Lifecycle.State.RESUMED)
+}
+
+@Composable
+internal fun RecyclerViewAndroidView(interopEnabled: Boolean) {
+    AndroidView(factory = { context ->
+        LayoutInflater.from(context)
+            .inflate(R.layout.android_in_compose_nested_scroll_interop, null)
+            .apply {
+                with(findViewById<RecyclerView>(R.id.main_list)) {
+                    layoutManager = LinearLayoutManager(
+                        context,
+                        RecyclerView.VERTICAL,
+                        false
+                    )
+                    adapter = NestedScrollInteropAdapter()
+                }
+            }.also {
+                ViewCompat.setNestedScrollingEnabled(it, interopEnabled)
+            }
+    }, modifier = Modifier.testTag(AndroidViewContainer))
+}
+
+private fun IntArray.toOffset() = Offset(this[0].toFloat(), this[1].toFloat())
+
+private fun IntArray.toComposeVelocity() =
+    Velocity((this[0] * -1).toFloat(), (this[1] * -1).toFloat())
+
+private fun IntArray.toReversedOffset(): Offset {
+    require(size == 2)
+    return Offset(this[0] * -1f, this[1] * -1f)
+}
+
+private fun FloatArray.toReversedVelocity(): Velocity {
+    require(size == 2)
+    return Velocity(this[0] * -1f, this[1] * -1f)
+}
+
+internal fun abs(velocity: Velocity) = Velocity(
+    kotlin.math.abs(velocity.x),
+    kotlin.math.abs(velocity.y)
+)
\ No newline at end of file
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/viewinterop/NestedScrollInteropThreeFoldTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/viewinterop/NestedScrollInteropThreeFoldTest.kt
new file mode 100644
index 0000000..b6add2f
--- /dev/null
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/viewinterop/NestedScrollInteropThreeFoldTest.kt
@@ -0,0 +1,288 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.viewinterop
+
+import android.os.Build
+import androidx.activity.ComponentActivity
+import androidx.annotation.RequiresApi
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.input.nestedscroll.nestedScroll
+import androidx.compose.ui.test.R
+import androidx.compose.ui.test.junit4.createAndroidComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.performTouchInput
+import androidx.compose.ui.test.swipeUp
+import androidx.compose.ui.unit.round
+import androidx.test.espresso.Espresso.onView
+import androidx.test.espresso.assertion.ViewAssertions.matches
+import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
+import androidx.test.espresso.matcher.ViewMatchers.withId
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@MediumTest
+@RequiresApi(Build.VERSION_CODES.M)
+@RunWith(AndroidJUnit4::class)
+class NestedScrollInteropThreeFoldTest {
+
+    @get:Rule
+    val rule = createAndroidComposeRule<ComponentActivity>()
+
+    private val nestedScrollParentView by lazy {
+        rule.activity.findViewById<TestNestedScrollParentView>(R.id.main_layout)
+    }
+
+    // Connection that sits after (below) the AndroidView
+    private val connection = InspectableNestedScrollConnection()
+    private val allConsumingConnection = AllConsumingInspectableConnection()
+
+    // CVC = Compose + View + Compose
+    // VCV = View + Compose + View
+    @Before
+    fun setUp() {
+        connection.reset()
+        allConsumingConnection.reset()
+    }
+
+    @Test
+    fun nestedScrollInteropIsOff_CVC_shouldNotPropagateCorrectly() {
+        // arrange
+        rule.setContent {
+            NestedScrollDeepNested(
+                modifier = Modifier.nestedScroll(connection),
+                enabled = false
+            )
+        }
+
+        // act
+        rule.onNodeWithTag(MainTestList).performTouchInput {
+            swipeUp()
+        }
+
+        // assert
+        rule.runOnIdle {
+            assertThat(connection.offeredFromChild).isEqualTo(Offset.Zero)
+        }
+    }
+
+    @Test
+    fun nestedScrollInteropIsOff_VCV_shouldNotPropagateCorrectly() {
+        // arrange
+        createViewComposeActivity(
+            outerModifier = Modifier.nestedScroll(connection),
+            enableInterop = false
+        ) {
+            RecyclerViewAndroidView(interopEnabled = false)
+        }
+
+        // act
+        rule.onNodeWithTag(AndroidViewContainer).performTouchInput {
+            swipeUp()
+        }
+
+        // assert
+        rule.runOnIdle {
+            assertThat(connection.offeredFromChild).isEqualTo(Offset.Zero)
+        }
+    }
+
+    @Test
+    fun nestedScrollInteropIsOn_CVC_shouldPropagateCorrectly() {
+        // arrange
+        rule.setContent {
+            NestedScrollDeepNested(
+                modifier = Modifier.nestedScroll(connection),
+                enabled = true
+            )
+        }
+
+        // act
+        rule.onNodeWithTag(MainTestList).performTouchInput {
+            swipeUp()
+        }
+
+        // assert
+        rule.runOnIdle {
+            assertThat(connection.offeredFromChild).isNotEqualTo(Offset.Zero)
+        }
+    }
+
+    @Test
+    fun nestedScrollInteropIsOn_VCV_shouldPropagateCorrectly() {
+        // arrange
+        createViewComposeActivity(
+            outerModifier = Modifier.nestedScroll(connection),
+            enableInterop = true
+        ) {
+            RecyclerViewAndroidView(interopEnabled = true)
+        }
+
+        // act
+        rule.onNodeWithTag(AndroidViewContainer).performTouchInput {
+            swipeUp()
+        }
+
+        // assert
+        rule.runOnIdle {
+            assertThat(connection.offeredFromChild).isNotEqualTo(Offset.Zero)
+        }
+    }
+
+    @Test
+    fun threeFoldNestedScrollCVC_composeConsumes_shouldPropagateCorrectly() {
+        // arrange
+        rule.setContent {
+            NestedScrollDeepNested(
+                modifier = Modifier.nestedScroll(allConsumingConnection),
+                enabled = true,
+                connection = connection
+            )
+        }
+
+        // act
+        rule.onNodeWithTag(MainTestList).performTouchInput {
+            swipeUp()
+        }
+
+        // assert
+        rule.runOnIdle {
+            assertThat(allConsumingConnection.offeredFromChild).isNotEqualTo(Offset.Zero)
+            assertThat(connection.consumedDownChain.round()).isEqualTo(Offset.Zero.round())
+        }
+    }
+
+    @Test
+    fun threeFoldNestedScrollVCV_composeConsumes_shouldPropagateCorrectly() {
+        // arrange
+        createViewComposeActivity(
+            outerModifier = Modifier.nestedScroll(allConsumingConnection),
+            enableInterop = true
+        ) {
+            RecyclerViewAndroidView(interopEnabled = true)
+        }
+
+        // act
+        rule.onNodeWithTag(AndroidViewContainer).performTouchInput {
+            swipeUp()
+        }
+
+        // assert
+        rule.waitForIdle()
+        assertThat(allConsumingConnection.offeredFromChild).isNotEqualTo(Offset.Zero)
+        onView(withId(R.id.fab))
+            .check(matches((isDisplayed())))
+    }
+
+    @Test
+    fun threeFoldNestedScrollCVC_composeDoesNotConsumes_shouldPropagateCorrectly() {
+        // arrange
+        val secondaryInspectableConnection = InspectableNestedScrollConnection()
+        rule.setContent {
+            NestedScrollDeepNested(
+                modifier = Modifier
+                    .nestedScroll(secondaryInspectableConnection),
+                enabled = true,
+                connection = connection
+            )
+        }
+
+        // act
+        rule.onNodeWithTag(MainTestList).performTouchInput {
+            swipeUp()
+        }
+
+        // assert
+        rule.runOnIdle {
+            assertThat(secondaryInspectableConnection.offeredFromChild).isNotEqualTo(Offset.Zero)
+            assertThat(connection.consumedDownChain).isNotEqualTo(Offset.Zero)
+        }
+    }
+
+    @Test
+    fun threeFoldNestedScrollCVC_composeDoesNotConsumes_checkDeltasAreCorrectForVelocity() {
+        // arrange
+        val secondaryInspectableConnection = InspectableNestedScrollConnection()
+        rule.setContent {
+            NestedScrollDeepNested(
+                modifier = Modifier
+                    .nestedScroll(secondaryInspectableConnection),
+                enabled = true,
+                connection = connection
+            )
+        }
+
+        // act
+        rule.onNodeWithTag(MainTestList).performTouchInput {
+            swipeUp()
+        }
+
+        // assert
+        rule.runOnIdle {
+            assertThat(secondaryInspectableConnection.velocityOfferedFromChild).isEqualTo(
+                connection.velocityConsumedDownChain
+            )
+        }
+    }
+
+    @Test
+    fun threeFoldNestedScrollVCV_composeDoesNotConsumes_checkDeltasAreCorrectForVelocity() {
+        // arrange
+        createViewComposeActivity(
+            outerModifier = Modifier.nestedScroll(connection),
+            enableInterop = true
+        ) {
+            RecyclerViewAndroidView(interopEnabled = true)
+        }
+
+        // act
+        rule.onNodeWithTag(AndroidViewContainer).performTouchInput {
+            swipeUp()
+        }
+
+        // assert
+        rule.runOnIdle {
+            assertThat(abs(nestedScrollParentView.velocityOfferedToParentOffset)).isEqualTo(
+                abs(connection.velocityConsumedDownChain)
+            )
+        }
+    }
+
+    @OptIn(ExperimentalComposeUiApi::class)
+    private fun createViewComposeActivity(
+        enableInterop: Boolean = true,
+        outerModifier: Modifier = Modifier,
+        content: @Composable () -> Unit
+    ) {
+        rule
+            .activityRule
+            .scenario
+            .createActivityWithComposeContent(
+                layout = R.layout.test_nested_scroll_coordinator_layout,
+                enableInterop = enableInterop,
+                content = content,
+                modifier = outerModifier
+            )
+    }
+}
\ No newline at end of file
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/viewinterop/NestedScrollInteropViewHolderTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/viewinterop/NestedScrollInteropViewHolderTest.kt
index 94d736af..78bd15c 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/viewinterop/NestedScrollInteropViewHolderTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/viewinterop/NestedScrollInteropViewHolderTest.kt
@@ -16,47 +16,18 @@
 
 package androidx.compose.ui.viewinterop
 
-import android.content.Context
 import android.os.Build
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.widget.TextView
 import androidx.annotation.RequiresApi
-import androidx.compose.foundation.background
 import androidx.compose.foundation.gestures.Orientation
 import androidx.compose.foundation.gestures.rememberScrollableState
 import androidx.compose.foundation.gestures.scrollable
 import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.lazy.LazyColumn
-import androidx.compose.material.Text
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
-import androidx.compose.ui.input.nestedscroll.NestedScrollSource
 import androidx.compose.ui.input.nestedscroll.nestedScroll
-import androidx.compose.ui.platform.ComposeView
-import androidx.compose.ui.platform.rememberNestedScrollInteropConnection
-import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.test.R
 import androidx.compose.ui.test.junit4.createComposeRule
-import androidx.compose.ui.test.onNodeWithTag
-import androidx.compose.ui.test.performTouchInput
-import androidx.compose.ui.test.swipeUp
 import androidx.compose.ui.unit.Velocity
-import androidx.compose.ui.unit.dp
-import androidx.core.view.ViewCompat
-import androidx.recyclerview.widget.LinearLayoutManager
-import androidx.recyclerview.widget.RecyclerView
-import androidx.recyclerview.widget.RecyclerView.OnFlingListener
 import androidx.test.espresso.Espresso
 import androidx.test.espresso.Espresso.onView
 import androidx.test.espresso.action.ViewActions.swipeUp
@@ -77,15 +48,13 @@
     @get:Rule
     val rule = createComposeRule()
 
-    private var consumedByRecyclerView = intArrayOf(0, 0)
-    private var velocityConsumedByRecyclerView = intArrayOf(0, 0)
     private val connection = InspectableNestedScrollConnection()
+    private val recyclerViewConsumptionTracker = RecyclerViewConsumptionTracker()
 
     @Before
     fun setUp() {
         connection.reset()
-        consumedByRecyclerView.fill(0)
-        velocityConsumedByRecyclerView.fill(0)
+        recyclerViewConsumptionTracker.reset()
     }
 
     @Test
@@ -94,7 +63,8 @@
         rule.setContent {
             NestedScrollInteropWithView(
                 modifier = Modifier.nestedScroll(connection),
-                enabled = false
+                enabled = false,
+                recyclerViewConsumptionTracker = recyclerViewConsumptionTracker
             )
         }
 
@@ -115,7 +85,8 @@
         rule.setContent {
             NestedScrollInteropWithView(
                 modifier = Modifier.nestedScroll(connection),
-                enabled = true
+                enabled = true,
+                recyclerViewConsumptionTracker = recyclerViewConsumptionTracker
             )
         }
 
@@ -134,7 +105,8 @@
         rule.setContent {
             NestedScrollInteropWithView(
                 modifier = Modifier.nestedScroll(connection),
-                enabled = true
+                enabled = true,
+                recyclerViewConsumptionTracker = recyclerViewConsumptionTracker
             )
         }
 
@@ -143,7 +115,8 @@
 
         // assert
         rule.runOnIdle {
-            assertThat(connection.offeredFromChild).isEqualTo(consumedByRecyclerView.toOffset())
+            assertThat(connection.offeredFromChild)
+                .isEqualTo(recyclerViewConsumptionTracker.deltaConsumed)
         }
     }
 
@@ -153,7 +126,8 @@
         rule.setContent {
             NestedScrollInteropWithView(
                 modifier = Modifier.nestedScroll(connection),
-                enabled = true
+                enabled = true,
+                recyclerViewConsumptionTracker = recyclerViewConsumptionTracker
             )
         }
 
@@ -163,7 +137,8 @@
         // assert
         rule.runOnIdle {
             assertThat(connection.notConsumedByChild).isEqualTo(Offset.Zero)
-            assertThat(connection.consumedDownChain).isEqualTo(consumedByRecyclerView.toOffset())
+            assertThat(connection.consumedDownChain)
+                .isEqualTo(recyclerViewConsumptionTracker.deltaConsumed)
         }
     }
 
@@ -176,7 +151,8 @@
             Box(modifier = Modifier.scrollable(controller, Orientation.Vertical)) {
                 NestedScrollInteropWithView(
                     modifier = Modifier.nestedScroll(connection),
-                    enabled = true
+                    enabled = true,
+                    recyclerViewConsumptionTracker = recyclerViewConsumptionTracker
                 )
             }
         }
@@ -188,60 +164,19 @@
 
         // assert
         rule.runOnIdle {
-            assertThat(consumedByRecyclerView.toOffset()).isEqualTo(Offset.Zero)
+            assertThat(recyclerViewConsumptionTracker.deltaConsumed).isEqualTo(Offset.Zero)
             assertThat(connection.notConsumedByChild).isEqualTo(Offset.Zero)
         }
     }
 
     @Test
-    fun nestedScrollInteropIsOff_threeFoldNestedScroll_shouldNotPropagateCorrectly() {
-        // arrange
-        rule.setContent {
-            NestedScrollDeepNested(
-                modifier = Modifier.nestedScroll(connection),
-                enabled = false
-            )
-        }
-
-        // act
-        rule.onNodeWithTag(MainTestList).performTouchInput {
-            swipeUp()
-        }
-
-        // assert
-        rule.runOnIdle {
-            assertThat(connection.offeredFromChild).isEqualTo(Offset.Zero)
-        }
-    }
-
-    @Test
-    fun nestedScrollInteropIsOn_threeFoldNestedScroll_shouldPropagateCorrectly() {
-        // arrange
-        rule.setContent {
-            NestedScrollDeepNested(
-                modifier = Modifier.nestedScroll(connection),
-                enabled = true
-            )
-        }
-
-        // act
-        rule.onNodeWithTag(MainTestList).performTouchInput {
-            swipeUp()
-        }
-
-        // assert
-        rule.runOnIdle {
-            assertThat(connection.offeredFromChild).isNotEqualTo(Offset.Zero)
-        }
-    }
-
-    @Test
     fun nestedScrollInteropIsOn_checkDeltasCorrectlyPropagatePreFling() {
         // arrange
         rule.setContent {
             NestedScrollInteropWithView(
                 modifier = Modifier.nestedScroll(connection),
-                enabled = true
+                enabled = true,
+                recyclerViewConsumptionTracker = recyclerViewConsumptionTracker
             )
         }
 
@@ -251,7 +186,7 @@
         // assert
         rule.runOnIdle {
             assertThat(abs(connection.velocityOfferedFromChild))
-                .isEqualTo(abs(velocityConsumedByRecyclerView.toComposeVelocity()))
+                .isEqualTo(abs(recyclerViewConsumptionTracker.velocityConsumed))
         }
     }
 
@@ -261,7 +196,8 @@
         rule.setContent {
             NestedScrollInteropWithView(
                 modifier = Modifier.nestedScroll(connection),
-                enabled = true
+                enabled = true,
+                recyclerViewConsumptionTracker = recyclerViewConsumptionTracker
             )
         }
 
@@ -272,180 +208,7 @@
         rule.runOnIdle {
             assertThat(connection.velocityNotConsumedByChild).isEqualTo(Velocity.Zero)
             assertThat(connection.velocityConsumedDownChain)
-                .isEqualTo(velocityConsumedByRecyclerView.toComposeVelocity())
+                .isEqualTo(recyclerViewConsumptionTracker.velocityConsumed)
         }
     }
-
-    @Composable
-    private fun NestedScrollInteropWithView(
-        modifier: Modifier = Modifier,
-        enabled: Boolean
-    ) {
-        NestedScrollInteropTestApp(modifier) { context ->
-            LayoutInflater.from(context)
-                .inflate(R.layout.android_in_compose_nested_scroll_interop, null)
-                .apply {
-                    with(findViewById<RecyclerView>(R.id.main_list)) {
-                        layoutManager = LinearLayoutManager(
-                            context,
-                            RecyclerView.VERTICAL,
-                            false
-                        )
-                        adapter = NestedScrollInteropAdapter()
-                        setOnScrollChangeListener { _, _, _, oldX, oldY ->
-                            consumedByRecyclerView[0] += oldX
-                            consumedByRecyclerView[1] += oldY
-                        }
-                        onFlingListener = object : OnFlingListener() {
-                            override fun onFling(velocityX: Int, velocityY: Int): Boolean {
-                                velocityConsumedByRecyclerView[0] += velocityX
-                                velocityConsumedByRecyclerView[1] += velocityY
-                                return false
-                            }
-                        }
-                    }
-                }.also {
-                    ViewCompat.setNestedScrollingEnabled(it, enabled)
-                }
-        }
-    }
-
-    @Composable
-    private fun NestedScrollDeepNested(modifier: Modifier, enabled: Boolean) {
-        // Box (Compose) + AndroidView (View) +
-        // ComposeInCooperatingViewNestedScrollInterop (Compose)
-        NestedScrollInteropTestApp(modifier) { context ->
-            LayoutInflater.from(context)
-                .inflate(R.layout.test_nested_scroll_coordinator_layout_without_toolbar, null)
-                .apply {
-                    with(findViewById<ComposeView>(R.id.compose_view)) {
-                        setContent {
-                            ComposeInCooperatingViewNestedScrollInterop(this)
-                        }
-                    }
-                }.also {
-                    ViewCompat.setNestedScrollingEnabled(it, enabled)
-                }
-        }
-    }
-}
-
-private const val MainTestList = "mainList"
-private const val AndroidViewContainer = "androidView"
-
-@Composable
-internal fun NestedScrollInteropTestApp(
-    modifier: Modifier = Modifier,
-    content: (Context) -> View
-) {
-    Box(modifier.fillMaxSize()) {
-        AndroidView(content, modifier = Modifier.testTag(AndroidViewContainer))
-    }
-}
-
-private class NestedScrollInteropAdapter :
-    RecyclerView.Adapter<NestedScrollInteropAdapter.SimpleTextViewHolder>() {
-    val items = (1..200).map { it.toString() }
-    override fun onCreateViewHolder(
-        parent: ViewGroup,
-        viewType: Int
-    ): SimpleTextViewHolder {
-        return SimpleTextViewHolder(
-            LayoutInflater.from(parent.context)
-                .inflate(
-                    R.layout.android_in_compose_nested_scroll_interop_list_item,
-                    parent,
-                    false
-                )
-        )
-    }
-
-    override fun onBindViewHolder(holder: SimpleTextViewHolder, position: Int) {
-        holder.bind(items[position])
-    }
-
-    override fun getItemCount(): Int = items.size
-
-    class SimpleTextViewHolder(view: View) : RecyclerView.ViewHolder(view) {
-        fun bind(item: String) {
-            itemView.findViewById<TextView>(R.id.list_item).text = item
-        }
-    }
-}
-
-@OptIn(ExperimentalComposeUiApi::class)
-@Composable
-private fun ComposeInCooperatingViewNestedScrollInterop(composeView: ComposeView) {
-    with(composeView) {
-        LazyColumn(
-            modifier = Modifier.nestedScroll(rememberNestedScrollInteropConnection(this))
-                .testTag(MainTestList)
-        ) {
-            items(200) { item ->
-                Box(
-                    modifier = Modifier
-                        .padding(16.dp)
-                        .height(56.dp)
-                        .fillMaxWidth()
-                        .background(Color.Gray),
-                    contentAlignment = Alignment.Center
-                ) {
-                    Text(item.toString())
-                }
-            }
-        }
-    }
-}
-
-private fun IntArray.toOffset() = Offset(this[0].toFloat(), this[1].toFloat())
-private fun IntArray.toComposeVelocity() =
-    Velocity((this[0] * -1).toFloat(), (this[1] * -1).toFloat())
-
-private fun abs(velocity: Velocity) = Velocity(
-    kotlin.math.abs(velocity.x),
-    kotlin.math.abs(velocity.y)
-)
-
-class InspectableNestedScrollConnection() : NestedScrollConnection {
-    var offeredFromChild = Offset.Zero
-    var velocityOfferedFromChild = Velocity.Zero
-    var consumedDownChain = Offset.Zero
-    var velocityConsumedDownChain = Velocity.Zero
-    var notConsumedByChild = Offset.Zero
-    var velocityNotConsumedByChild = Velocity.Zero
-
-    override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
-        offeredFromChild += available
-        return Offset.Zero
-    }
-
-    override fun onPostScroll(
-        consumed: Offset,
-        available: Offset,
-        source: NestedScrollSource
-    ): Offset {
-        consumedDownChain += consumed
-        notConsumedByChild += available
-        return Offset.Zero
-    }
-
-    override suspend fun onPreFling(available: Velocity): Velocity {
-        velocityOfferedFromChild += available
-        return Velocity.Zero
-    }
-
-    override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity {
-        velocityConsumedDownChain += consumed
-        velocityNotConsumedByChild += available
-        return Velocity.Zero
-    }
-
-    fun reset() {
-        offeredFromChild = Offset.Zero
-        velocityOfferedFromChild = Velocity.Zero
-        consumedDownChain = Offset.Zero
-        velocityConsumedDownChain = Velocity.Zero
-        notConsumedByChild = Offset.Zero
-        velocityNotConsumedByChild = Velocity.Zero
-    }
 }
\ No newline at end of file
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/NestedScrollInteropConnection.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/NestedScrollInteropConnection.kt
index 663116b..8f2c070 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/NestedScrollInteropConnection.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/NestedScrollInteropConnection.kt
@@ -16,6 +16,7 @@
 
 package androidx.compose.ui.platform
 
+import android.view.View
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.remember
 import androidx.compose.ui.ExperimentalComposeUiApi
@@ -40,7 +41,7 @@
  * deltas from dispatching children on the Compose side.
  */
 internal class NestedScrollInteropConnection(
-    private val view: ComposeView
+    private val view: View
 ) : NestedScrollConnection {
 
     private val nestedScrollChildHelper = NestedScrollingChildHelper(view).apply {
@@ -225,13 +226,12 @@
  * Learn how to enable nested scroll interop:
  * @sample androidx.compose.ui.samples.ComposeInCooperatingViewNestedScrollInteropSample
  *
- * @param view The [ComposeView] that hosts the [Composable] during Compose in View interop.
- *
  */
 @ExperimentalComposeUiApi
 @Composable
-fun rememberNestedScrollInteropConnection(
-    view: ComposeView
-): NestedScrollConnection = remember(view) {
-    NestedScrollInteropConnection(view)
+fun rememberNestedScrollInteropConnection(): NestedScrollConnection {
+    val composeView = LocalView.current
+    return remember(composeView) {
+        NestedScrollInteropConnection(composeView)
+    }
 }
\ No newline at end of file
diff --git a/docs-public/build.gradle b/docs-public/build.gradle
index f39eb9c..d044e2e 100644
--- a/docs-public/build.gradle
+++ b/docs-public/build.gradle
@@ -23,10 +23,10 @@
     docs("androidx.arch.core:core-testing:2.1.0")
     docs("androidx.asynclayoutinflater:asynclayoutinflater:1.0.0")
     docs("androidx.autofill:autofill:1.2.0-beta01")
-    docs("androidx.benchmark:benchmark-common:1.1.0-beta06")
-    docs("androidx.benchmark:benchmark-junit4:1.1.0-beta06")
-    docs("androidx.benchmark:benchmark-macro:1.1.0-beta06")
-    docs("androidx.benchmark:benchmark-macro-junit4:1.1.0-beta06")
+    docs("androidx.benchmark:benchmark-common:1.1.0-rc01")
+    docs("androidx.benchmark:benchmark-junit4:1.1.0-rc01")
+    docs("androidx.benchmark:benchmark-macro:1.1.0-rc01")
+    docs("androidx.benchmark:benchmark-macro-junit4:1.1.0-rc01")
     docs("androidx.biometric:biometric:1.2.0-alpha04")
     docs("androidx.biometric:biometric-ktx:1.2.0-alpha04")
     samples("androidx.biometric:biometric-ktx-samples:1.2.0-alpha04")
@@ -46,52 +46,54 @@
     docs("androidx.cardview:cardview:1.0.0")
     docs("androidx.collection:collection:1.2.0")
     docs("androidx.collection:collection-ktx:1.2.0")
-    docs("androidx.compose.animation:animation:1.2.0-alpha07")
-    docs("androidx.compose.animation:animation-core:1.2.0-alpha07")
-    docs("androidx.compose.animation:animation-graphics:1.2.0-alpha07")
-    samples("androidx.compose.animation:animation-samples:1.2.0-alpha07")
-    samples("androidx.compose.animation:animation-core-samples:1.2.0-alpha07")
-    samples("androidx.compose.animation:animation-graphics-samples:1.2.0-alpha07")
-    docs("androidx.compose.foundation:foundation:1.2.0-alpha07")
-    docs("androidx.compose.foundation:foundation-layout:1.2.0-alpha07")
-    samples("androidx.compose.foundation:foundation-layout-samples:1.2.0-alpha07")
-    samples("androidx.compose.foundation:foundation-samples:1.2.0-alpha07")
-    docs("androidx.compose.material3:material3:1.0.0-alpha09")
-    samples("androidx.compose.material3:material3-samples:1.0.0-alpha09")
-    docs("androidx.compose.material:material:1.2.0-alpha07")
-    docs("androidx.compose.material:material-icons-core:1.2.0-alpha07")
-    samples("androidx.compose.material:material-icons-core-samples:1.2.0-alpha07")
-    docs("androidx.compose.material:material-ripple:1.2.0-alpha07")
-    samples("androidx.compose.material:material-samples:1.2.0-alpha07")
-    docs("androidx.compose.runtime:runtime:1.2.0-alpha07")
-    docs("androidx.compose.runtime:runtime-livedata:1.2.0-alpha07")
-    samples("androidx.compose.runtime:runtime-livedata-samples:1.2.0-alpha07")
-    docs("androidx.compose.runtime:runtime-rxjava2:1.2.0-alpha07")
-    samples("androidx.compose.runtime:runtime-rxjava2-samples:1.2.0-alpha07")
-    docs("androidx.compose.runtime:runtime-rxjava3:1.2.0-alpha07")
-    samples("androidx.compose.runtime:runtime-rxjava3-samples:1.2.0-alpha07")
-    docs("androidx.compose.runtime:runtime-saveable:1.2.0-alpha07")
-    samples("androidx.compose.runtime:runtime-saveable-samples:1.2.0-alpha07")
-    samples("androidx.compose.runtime:runtime-samples:1.2.0-alpha07")
-    docs("androidx.compose.ui:ui:1.2.0-alpha07")
-    docs("androidx.compose.ui:ui-geometry:1.2.0-alpha07")
-    docs("androidx.compose.ui:ui-graphics:1.2.0-alpha07")
-    samples("androidx.compose.ui:ui-graphics-samples:1.2.0-alpha07")
-    docs("androidx.compose.ui:ui-test:1.2.0-alpha07")
-    docs("androidx.compose.ui:ui-test-junit4:1.2.0-alpha07")
-    samples("androidx.compose.ui:ui-test-samples:1.2.0-alpha07")
-    docs("androidx.compose.ui:ui-text:1.2.0-alpha07")
-    docs("androidx.compose.ui:ui-text-google-fonts:1.2.0-alpha07")
-    samples("androidx.compose.ui:ui-text-samples:1.2.0-alpha07")
-    docs("androidx.compose.ui:ui-tooling:1.2.0-alpha07")
-    docs("androidx.compose.ui:ui-tooling-data:1.2.0-alpha07")
-    docs("androidx.compose.ui:ui-tooling-preview:1.2.0-alpha07")
-    docs("androidx.compose.ui:ui-unit:1.2.0-alpha07")
-    samples("androidx.compose.ui:ui-unit-samples:1.2.0-alpha07")
-    docs("androidx.compose.ui:ui-util:1.2.0-alpha07")
-    docs("androidx.compose.ui:ui-viewbinding:1.2.0-alpha07")
-    samples("androidx.compose.ui:ui-viewbinding-samples:1.2.0-alpha07")
-    samples("androidx.compose.ui:ui-samples:1.2.0-alpha07")
+    docs("androidx.compose.animation:animation:1.2.0-alpha08")
+    docs("androidx.compose.animation:animation-core:1.2.0-alpha08")
+    docs("androidx.compose.animation:animation-graphics:1.2.0-alpha08")
+    samples("androidx.compose.animation:animation-samples:1.2.0-alpha08")
+    samples("androidx.compose.animation:animation-core-samples:1.2.0-alpha08")
+    samples("androidx.compose.animation:animation-graphics-samples:1.2.0-alpha08")
+    docs("androidx.compose.foundation:foundation:1.2.0-alpha08")
+    docs("androidx.compose.foundation:foundation-layout:1.2.0-alpha08")
+    samples("androidx.compose.foundation:foundation-layout-samples:1.2.0-alpha08")
+    samples("androidx.compose.foundation:foundation-samples:1.2.0-alpha08")
+    docs("androidx.compose.material3:material3:1.0.0-alpha10")
+    samples("androidx.compose.material3:material3-samples:1.0.0-alpha10")
+    docs("androidx.compose.material3:material3-window-size-class:1.0.0-alpha10")
+    samples("androidx.compose.material3:material3-window-size-class-samples:1.0.0-alpha10")
+    docs("androidx.compose.material:material:1.2.0-alpha08")
+    docs("androidx.compose.material:material-icons-core:1.2.0-alpha08")
+    samples("androidx.compose.material:material-icons-core-samples:1.2.0-alpha08")
+    docs("androidx.compose.material:material-ripple:1.2.0-alpha08")
+    samples("androidx.compose.material:material-samples:1.2.0-alpha08")
+    docs("androidx.compose.runtime:runtime:1.2.0-alpha08")
+    docs("androidx.compose.runtime:runtime-livedata:1.2.0-alpha08")
+    samples("androidx.compose.runtime:runtime-livedata-samples:1.2.0-alpha08")
+    docs("androidx.compose.runtime:runtime-rxjava2:1.2.0-alpha08")
+    samples("androidx.compose.runtime:runtime-rxjava2-samples:1.2.0-alpha08")
+    docs("androidx.compose.runtime:runtime-rxjava3:1.2.0-alpha08")
+    samples("androidx.compose.runtime:runtime-rxjava3-samples:1.2.0-alpha08")
+    docs("androidx.compose.runtime:runtime-saveable:1.2.0-alpha08")
+    samples("androidx.compose.runtime:runtime-saveable-samples:1.2.0-alpha08")
+    samples("androidx.compose.runtime:runtime-samples:1.2.0-alpha08")
+    docs("androidx.compose.ui:ui:1.2.0-alpha08")
+    docs("androidx.compose.ui:ui-geometry:1.2.0-alpha08")
+    docs("androidx.compose.ui:ui-graphics:1.2.0-alpha08")
+    samples("androidx.compose.ui:ui-graphics-samples:1.2.0-alpha08")
+    docs("androidx.compose.ui:ui-test:1.2.0-alpha08")
+    docs("androidx.compose.ui:ui-test-junit4:1.2.0-alpha08")
+    samples("androidx.compose.ui:ui-test-samples:1.2.0-alpha08")
+    docs("androidx.compose.ui:ui-text:1.2.0-alpha08")
+    docs("androidx.compose.ui:ui-text-google-fonts:1.2.0-alpha08")
+    samples("androidx.compose.ui:ui-text-samples:1.2.0-alpha08")
+    docs("androidx.compose.ui:ui-tooling:1.2.0-alpha08")
+    docs("androidx.compose.ui:ui-tooling-data:1.2.0-alpha08")
+    docs("androidx.compose.ui:ui-tooling-preview:1.2.0-alpha08")
+    docs("androidx.compose.ui:ui-unit:1.2.0-alpha08")
+    samples("androidx.compose.ui:ui-unit-samples:1.2.0-alpha08")
+    docs("androidx.compose.ui:ui-util:1.2.0-alpha08")
+    docs("androidx.compose.ui:ui-viewbinding:1.2.0-alpha08")
+    samples("androidx.compose.ui:ui-viewbinding-samples:1.2.0-alpha08")
+    samples("androidx.compose.ui:ui-samples:1.2.0-alpha08")
     docs("androidx.concurrent:concurrent-futures:1.1.0")
     docs("androidx.concurrent:concurrent-futures-ktx:1.1.0")
     docs("androidx.contentpager:contentpager:1.0.0")
@@ -101,10 +103,10 @@
     samples("androidx.core:core-performance-samples:1.0.0-alpha02")
     docs("androidx.core:core-remoteviews:1.0.0-alpha03")
     docs("androidx.core:core-role:1.1.0-rc01")
-    docs("androidx.core:core-animation:1.0.0-alpha02")
+    docs("androidx.core:core-animation:1.0.0-beta01")
     docs("androidx.core:core-animation-testing:1.0.0-alpha02")
-    docs("androidx.core:core:1.8.0-alpha07")
-    docs("androidx.core:core-ktx:1.8.0-alpha07")
+    docs("androidx.core:core:1.8.0-beta01")
+    docs("androidx.core:core-ktx:1.8.0-beta01")
     docs("androidx.core:core-splashscreen:1.0.0-beta02")
     docs("androidx.cursoradapter:cursoradapter:1.0.0")
     docs("androidx.customview:customview:1.2.0-alpha01")
@@ -118,23 +120,23 @@
     docs("androidx.datastore:datastore-rxjava2:1.0.0")
     docs("androidx.datastore:datastore-rxjava3:1.0.0")
     docs("androidx.documentfile:documentfile:1.1.0-alpha01")
-    docs("androidx.draganddrop:draganddrop:1.0.0-beta01")
+    docs("androidx.draganddrop:draganddrop:1.0.0-rc01")
     docs("androidx.drawerlayout:drawerlayout:1.1.1")
     docs("androidx.dynamicanimation:dynamicanimation:1.1.0-alpha02")
     docs("androidx.dynamicanimation:dynamicanimation-ktx:1.0.0-alpha03")
-    docs("androidx.emoji2:emoji2:1.2.0-alpha03")
-    docs("androidx.emoji2:emoji2-bundled:1.2.0-alpha03")
-    docs("androidx.emoji2:emoji2-views:1.2.0-alpha03")
-    docs("androidx.emoji2:emoji2-views-helper:1.2.0-alpha03")
+    docs("androidx.emoji2:emoji2:1.2.0-alpha04")
+    docs("androidx.emoji2:emoji2-bundled:1.2.0-alpha04")
+    docs("androidx.emoji2:emoji2-views:1.2.0-alpha04")
+    docs("androidx.emoji2:emoji2-views-helper:1.2.0-alpha04")
     docs("androidx.emoji:emoji:1.2.0-alpha03")
     docs("androidx.emoji:emoji-appcompat:1.2.0-alpha03")
     docs("androidx.emoji:emoji-bundled:1.2.0-alpha03")
     docs("androidx.enterprise:enterprise-feedback:1.1.0")
     docs("androidx.enterprise:enterprise-feedback-testing:1.1.0")
     docs("androidx.exifinterface:exifinterface:1.3.3")
-    docs("androidx.fragment:fragment:1.5.0-alpha05")
-    docs("androidx.fragment:fragment-ktx:1.5.0-alpha05")
-    docs("androidx.fragment:fragment-testing:1.5.0-alpha05")
+    docs("androidx.fragment:fragment:1.5.0-beta01")
+    docs("androidx.fragment:fragment-ktx:1.5.0-beta01")
+    docs("androidx.fragment:fragment-testing:1.5.0-beta01")
     docs("androidx.glance:glance:1.0.0-alpha03")
     docs("androidx.glance:glance-appwidget:1.0.0-alpha03")
     docs("androidx.glance:glance-appwidget-proto:1.0.0-alpha03")
@@ -179,23 +181,23 @@
     docs("androidx.media2:media2-player:1.2.1")
     docs("androidx.media2:media2-session:1.2.1")
     docs("androidx.media2:media2-widget:1.2.1")
-    docs("androidx.media:media:1.6.0-rc01")
-    docs("androidx.mediarouter:mediarouter:1.3.0-rc01")
-    docs("androidx.mediarouter:mediarouter-testing:1.3.0-rc01")
+    docs("androidx.media:media:1.6.0")
+    docs("androidx.mediarouter:mediarouter:1.3.0")
+    docs("androidx.mediarouter:mediarouter-testing:1.3.0")
     docs("androidx.metrics:metrics-performance:1.0.0-alpha01")
-    docs("androidx.navigation:navigation-common:2.5.0-alpha04")
-    docs("androidx.navigation:navigation-common-ktx:2.5.0-alpha04")
-    docs("androidx.navigation:navigation-compose:2.5.0-alpha04")
-    samples("androidx.navigation:navigation-compose-samples:2.5.0-alpha04")
-    docs("androidx.navigation:navigation-dynamic-features-fragment:2.5.0-alpha04")
-    docs("androidx.navigation:navigation-dynamic-features-runtime:2.5.0-alpha04")
-    docs("androidx.navigation:navigation-fragment:2.5.0-alpha04")
-    docs("androidx.navigation:navigation-fragment-ktx:2.5.0-alpha04")
-    docs("androidx.navigation:navigation-runtime:2.5.0-alpha04")
-    docs("androidx.navigation:navigation-runtime-ktx:2.5.0-alpha04")
-    docs("androidx.navigation:navigation-testing:2.5.0-alpha04")
-    docs("androidx.navigation:navigation-ui:2.5.0-alpha04")
-    docs("androidx.navigation:navigation-ui-ktx:2.5.0-alpha04")
+    docs("androidx.navigation:navigation-common:2.5.0-beta01")
+    docs("androidx.navigation:navigation-common-ktx:2.5.0-beta01")
+    docs("androidx.navigation:navigation-compose:2.5.0-beta01")
+    samples("androidx.navigation:navigation-compose-samples:2.5.0-beta01")
+    docs("androidx.navigation:navigation-dynamic-features-fragment:2.5.0-beta01")
+    docs("androidx.navigation:navigation-dynamic-features-runtime:2.5.0-beta01")
+    docs("androidx.navigation:navigation-fragment:2.5.0-beta01")
+    docs("androidx.navigation:navigation-fragment-ktx:2.5.0-beta01")
+    docs("androidx.navigation:navigation-runtime:2.5.0-beta01")
+    docs("androidx.navigation:navigation-runtime-ktx:2.5.0-beta01")
+    docs("androidx.navigation:navigation-testing:2.5.0-beta01")
+    docs("androidx.navigation:navigation-ui:2.5.0-beta01")
+    docs("androidx.navigation:navigation-ui-ktx:2.5.0-beta01")
     docs("androidx.paging:paging-common:3.1.1")
     docs("androidx.paging:paging-common-ktx:3.1.1")
     docs("androidx.paging:paging-compose:1.0.0-alpha15")
@@ -253,39 +255,39 @@
     docs("androidx.transition:transition:1.4.1")
     docs("androidx.transition:transition-ktx:1.4.1")
     docs("androidx.tvprovider:tvprovider:1.1.0-alpha01")
-    docs("androidx.vectordrawable:vectordrawable:1.2.0-alpha02")
+    docs("androidx.vectordrawable:vectordrawable:1.2.0-beta01")
     docs("androidx.vectordrawable:vectordrawable-animated:1.2.0-alpha01")
-    docs("androidx.vectordrawable:vectordrawable-seekable:1.0.0-alpha02")
+    docs("androidx.vectordrawable:vectordrawable-seekable:1.0.0-beta01")
     docs("androidx.versionedparcelable:versionedparcelable:1.1.1")
     docs("androidx.viewpager2:viewpager2:1.1.0-beta01")
     docs("androidx.viewpager:viewpager:1.1.0-alpha01")
-    docs("androidx.wear.compose:compose-foundation:1.0.0-alpha20")
-    samples("androidx.wear.compose:compose-foundation-samples:1.0.0-alpha20")
-    docs("androidx.wear.compose:compose-material:1.0.0-alpha20")
-    samples("androidx.wear.compose:compose-material-samples:1.0.0-alpha20")
-    docs("androidx.wear.compose:compose-navigation:1.0.0-alpha20")
-    samples("androidx.wear.compose:compose-navigation-samples:1.0.0-alpha20")
+    docs("androidx.wear.compose:compose-foundation:1.0.0-alpha21")
+    samples("androidx.wear.compose:compose-foundation-samples:1.0.0-alpha21")
+    docs("androidx.wear.compose:compose-material:1.0.0-alpha21")
+    samples("androidx.wear.compose:compose-material-samples:1.0.0-alpha21")
+    docs("androidx.wear.compose:compose-navigation:1.0.0-alpha21")
+    samples("androidx.wear.compose:compose-navigation-samples:1.0.0-alpha21")
     docs("androidx.wear.tiles:tiles:1.1.0-alpha05")
     docs("androidx.wear.tiles:tiles-material:1.1.0-alpha05")
     docs("androidx.wear.tiles:tiles-proto:1.1.0-alpha05")
     docs("androidx.wear.tiles:tiles-renderer:1.1.0-alpha05")
     docs("androidx.wear.tiles:tiles-testing:1.1.0-alpha05")
-    docs("androidx.wear.watchface:watchface:1.1.0-alpha05")
-    docs("androidx.wear.watchface:watchface-client:1.1.0-alpha05")
-    docs("androidx.wear.watchface:watchface-client-guava:1.1.0-alpha05")
-    docs("androidx.wear.watchface:watchface-complications:1.1.0-alpha05")
-    docs("androidx.wear.watchface:watchface-complications-data:1.1.0-alpha05")
-    docs("androidx.wear.watchface:watchface-complications-data-source:1.1.0-alpha05")
-    docs("androidx.wear.watchface:watchface-complications-data-source-ktx:1.1.0-alpha05")
-    samples("androidx.wear.watchface:watchface-complications-permission-dialogs-sample:1.1.0-alpha05")
-    docs("androidx.wear.watchface:watchface-complications-rendering:1.1.0-alpha05")
-    docs("androidx.wear.watchface:watchface-data:1.1.0-alpha05")
-    docs("androidx.wear.watchface:watchface-editor:1.1.0-alpha05")
-    docs("androidx.wear.watchface:watchface-editor-guava:1.1.0-alpha05")
-    samples("androidx.wear.watchface:watchface-editor-samples:1.1.0-alpha05")
-    docs("androidx.wear.watchface:watchface-guava:1.1.0-alpha05")
-    samples("androidx.wear.watchface:watchface-samples:1.1.0-alpha05")
-    docs("androidx.wear.watchface:watchface-style:1.1.0-alpha05")
+    docs("androidx.wear.watchface:watchface:1.1.0-beta01")
+    docs("androidx.wear.watchface:watchface-client:1.1.0-beta01")
+    docs("androidx.wear.watchface:watchface-client-guava:1.1.0-beta01")
+    docs("androidx.wear.watchface:watchface-complications:1.1.0-beta01")
+    docs("androidx.wear.watchface:watchface-complications-data:1.1.0-beta01")
+    docs("androidx.wear.watchface:watchface-complications-data-source:1.1.0-beta01")
+    docs("androidx.wear.watchface:watchface-complications-data-source-ktx:1.1.0-beta01")
+    samples("androidx.wear.watchface:watchface-complications-permission-dialogs-sample:1.1.0-beta01")
+    docs("androidx.wear.watchface:watchface-complications-rendering:1.1.0-beta01")
+    docs("androidx.wear.watchface:watchface-data:1.1.0-beta01")
+    docs("androidx.wear.watchface:watchface-editor:1.1.0-beta01")
+    docs("androidx.wear.watchface:watchface-editor-guava:1.1.0-beta01")
+    samples("androidx.wear.watchface:watchface-editor-samples:1.1.0-beta01")
+    docs("androidx.wear.watchface:watchface-guava:1.1.0-beta01")
+    samples("androidx.wear.watchface:watchface-samples:1.1.0-beta01")
+    docs("androidx.wear.watchface:watchface-style:1.1.0-beta01")
     docs("androidx.wear:wear:1.3.0-alpha02")
     stubs(fileTree(dir: "../wear/wear_stubs/", include: ["com.google.android.wearable-stubs.jar"]))
     docs("androidx.wear:wear-ongoing:1.1.0-alpha01")
@@ -295,7 +297,7 @@
     samples("androidx.wear:wear-input-samples:1.2.0-alpha01")
     docs("androidx.wear:wear-input-testing:1.2.0-alpha02")
     docs("androidx.webkit:webkit:1.4.0")
-    docs("androidx.window:window:1.0.0")
+    docs("androidx.window:window:1.1.0-alpha01")
     stubs(fileTree(dir: "../window/stubs/", include: ["window-sidecar-release-0.1.0-alpha01.aar"]))
     stubs("androidx.window:window-extensions:1.0.0-alpha01")
     docs("androidx.window:window-java:1.0.0")
diff --git a/docs-tip-of-tree/build.gradle b/docs-tip-of-tree/build.gradle
index 7a9ccff..c187e5f 100644
--- a/docs-tip-of-tree/build.gradle
+++ b/docs-tip-of-tree/build.gradle
@@ -46,7 +46,8 @@
     docs(project(":car:app:app-projected"))
     docs(project(":car:app:app-testing"))
     docs(project(":cardview:cardview"))
-    docs(project(":collection2:collection2"))
+    docs(project(":collection:collection"))
+    docs(project(":collection:collection-ktx"))
     docs(project(":compose:animation:animation"))
     docs(project(":compose:animation:animation-core"))
     docs(project(":compose:animation:animation-graphics"))
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index d4127fd..749a003 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -37,6 +37,7 @@
 ksp = "1.6.20-1.0.4"
 ktlint = "0.43.0"
 leakcanary = "2.7"
+metalava = "1.0.0-alpha06"
 mockito = "2.25.0"
 skiko = "0.7.7"
 sqldelight = "1.3.0"
@@ -135,7 +136,7 @@
 leakcanary = { module = "com.squareup.leakcanary:leakcanary-android", version.ref = "leakcanary" }
 leakcanaryInstrumentation = { module = "com.squareup.leakcanary:leakcanary-android-instrumentation", version.ref = "leakcanary" }
 material = { module = "com.google.android.material:material", version = "1.2.1" }
-metalava = { module = "com.android.tools.metalava:metalava", version = "1.0.0-alpha06" }
+metalava = { module = "com.android.tools.metalava:metalava", version.ref = "metalava" }
 mlkitBarcode = { module = "com.google.mlkit:barcode-scanning", version = "17.0.2" }
 mockitoCore = { module = "org.mockito:mockito-core", version.ref = "mockito" }
 mockitoAndroid = { module = "org.mockito:mockito-android", version.ref = "mockito" }
diff --git a/health/health-connect-client/api/current.txt b/health/health-connect-client/api/current.txt
index aea504e..ea47005 100644
--- a/health/health-connect-client/api/current.txt
+++ b/health/health-connect-client/api/current.txt
@@ -8,9 +8,12 @@
     method public suspend Object? getChangesToken(androidx.health.connect.client.request.ChangesTokenRequest request, kotlin.coroutines.Continuation<? super java.lang.String>);
     method public default static androidx.health.connect.client.HealthConnectClient getOrCreate(android.content.Context context, optional java.util.List<java.lang.String> packageNames);
     method public default static androidx.health.connect.client.HealthConnectClient getOrCreate(android.content.Context context);
+    method public androidx.health.connect.client.PermissionController getPermissionController();
     method public suspend Object? insertRecords(java.util.List<? extends androidx.health.connect.client.records.Record> records, kotlin.coroutines.Continuation<? super androidx.health.connect.client.response.InsertRecordsResponse>);
     method public default static boolean isAvailable(android.content.Context context, optional java.util.List<java.lang.String> packageNames);
     method public default static boolean isAvailable(android.content.Context context);
+    method public suspend <T extends androidx.health.connect.client.records.Record> Object? readRecords(androidx.health.connect.client.request.ReadRecordsRequest<T> request, kotlin.coroutines.Continuation<? super androidx.health.connect.client.response.ReadRecordsResponse<T>>);
+    property public abstract androidx.health.connect.client.PermissionController permissionController;
     field public static final androidx.health.connect.client.HealthConnectClient.Companion Companion;
   }
 
@@ -21,6 +24,11 @@
     method public boolean isAvailable(android.content.Context context);
   }
 
+  public interface PermissionController {
+    method public suspend Object? getGrantedPermissions(java.util.Set<androidx.health.connect.client.permission.Permission> permissions, kotlin.coroutines.Continuation<? super java.util.Set<? extends androidx.health.connect.client.permission.Permission>>);
+    method public suspend Object? revokeAllPermissions(kotlin.coroutines.Continuation<? super kotlin.Unit>);
+  }
+
 }
 
 package androidx.health.connect.client.changes {
@@ -91,6 +99,28 @@
 
 }
 
+package androidx.health.connect.client.permission {
+
+  public final class HealthDataRequestPermissions extends androidx.activity.result.contract.ActivityResultContract<java.util.Set<? extends androidx.health.connect.client.permission.Permission>,java.util.Set<? extends androidx.health.connect.client.permission.Permission>> {
+    ctor public HealthDataRequestPermissions(optional String providerPackageName);
+    method public android.content.Intent createIntent(android.content.Context context, java.util.Set<androidx.health.connect.client.permission.Permission> input);
+    method public androidx.activity.result.contract.ActivityResultContract.SynchronousResult<java.util.Set<androidx.health.connect.client.permission.Permission>>? getSynchronousResult(android.content.Context context, java.util.Set<androidx.health.connect.client.permission.Permission> input);
+    method public java.util.Set<androidx.health.connect.client.permission.Permission> parseResult(int resultCode, android.content.Intent? intent);
+  }
+
+  public final class Permission {
+    method public static androidx.health.connect.client.permission.Permission createReadPermission(kotlin.reflect.KClass<? extends androidx.health.connect.client.records.Record> recordType);
+    method public static androidx.health.connect.client.permission.Permission createWritePermission(kotlin.reflect.KClass<? extends androidx.health.connect.client.records.Record> recordType);
+    field public static final androidx.health.connect.client.permission.Permission.Companion Companion;
+  }
+
+  public static final class Permission.Companion {
+    method public androidx.health.connect.client.permission.Permission createReadPermission(kotlin.reflect.KClass<? extends androidx.health.connect.client.records.Record> recordType);
+    method public androidx.health.connect.client.permission.Permission createWritePermission(kotlin.reflect.KClass<? extends androidx.health.connect.client.records.Record> recordType);
+  }
+
+}
+
 package androidx.health.connect.client.records {
 
   public final class ActivityTypes {
@@ -327,6 +357,10 @@
     ctor public ChangesTokenRequest(java.util.Set<? extends kotlin.reflect.KClass<? extends androidx.health.connect.client.records.Record>> recordTypes, optional java.util.Set<androidx.health.connect.client.metadata.DataOrigin> dataOriginFilters);
   }
 
+  public final class ReadRecordsRequest<T extends androidx.health.connect.client.records.Record> {
+    ctor public ReadRecordsRequest(kotlin.reflect.KClass<T> recordType, androidx.health.connect.client.time.TimeRangeFilter timeRangeFilter, optional java.util.List<androidx.health.connect.client.metadata.DataOrigin> dataOriginFilter, optional boolean ascendingOrder, optional int pageSize, optional String? pageToken);
+  }
+
 }
 
 package androidx.health.connect.client.response {
@@ -347,6 +381,13 @@
     property public final java.util.List<java.lang.String> recordUidsList;
   }
 
+  public final class ReadRecordsResponse<T extends androidx.health.connect.client.records.Record> {
+    method public String? getPageToken();
+    method public java.util.List<T> getRecords();
+    property public final String? pageToken;
+    property public final java.util.List<T> records;
+  }
+
 }
 
 package androidx.health.connect.client.time {
diff --git a/health/health-connect-client/api/public_plus_experimental_current.txt b/health/health-connect-client/api/public_plus_experimental_current.txt
index aea504e..ea47005 100644
--- a/health/health-connect-client/api/public_plus_experimental_current.txt
+++ b/health/health-connect-client/api/public_plus_experimental_current.txt
@@ -8,9 +8,12 @@
     method public suspend Object? getChangesToken(androidx.health.connect.client.request.ChangesTokenRequest request, kotlin.coroutines.Continuation<? super java.lang.String>);
     method public default static androidx.health.connect.client.HealthConnectClient getOrCreate(android.content.Context context, optional java.util.List<java.lang.String> packageNames);
     method public default static androidx.health.connect.client.HealthConnectClient getOrCreate(android.content.Context context);
+    method public androidx.health.connect.client.PermissionController getPermissionController();
     method public suspend Object? insertRecords(java.util.List<? extends androidx.health.connect.client.records.Record> records, kotlin.coroutines.Continuation<? super androidx.health.connect.client.response.InsertRecordsResponse>);
     method public default static boolean isAvailable(android.content.Context context, optional java.util.List<java.lang.String> packageNames);
     method public default static boolean isAvailable(android.content.Context context);
+    method public suspend <T extends androidx.health.connect.client.records.Record> Object? readRecords(androidx.health.connect.client.request.ReadRecordsRequest<T> request, kotlin.coroutines.Continuation<? super androidx.health.connect.client.response.ReadRecordsResponse<T>>);
+    property public abstract androidx.health.connect.client.PermissionController permissionController;
     field public static final androidx.health.connect.client.HealthConnectClient.Companion Companion;
   }
 
@@ -21,6 +24,11 @@
     method public boolean isAvailable(android.content.Context context);
   }
 
+  public interface PermissionController {
+    method public suspend Object? getGrantedPermissions(java.util.Set<androidx.health.connect.client.permission.Permission> permissions, kotlin.coroutines.Continuation<? super java.util.Set<? extends androidx.health.connect.client.permission.Permission>>);
+    method public suspend Object? revokeAllPermissions(kotlin.coroutines.Continuation<? super kotlin.Unit>);
+  }
+
 }
 
 package androidx.health.connect.client.changes {
@@ -91,6 +99,28 @@
 
 }
 
+package androidx.health.connect.client.permission {
+
+  public final class HealthDataRequestPermissions extends androidx.activity.result.contract.ActivityResultContract<java.util.Set<? extends androidx.health.connect.client.permission.Permission>,java.util.Set<? extends androidx.health.connect.client.permission.Permission>> {
+    ctor public HealthDataRequestPermissions(optional String providerPackageName);
+    method public android.content.Intent createIntent(android.content.Context context, java.util.Set<androidx.health.connect.client.permission.Permission> input);
+    method public androidx.activity.result.contract.ActivityResultContract.SynchronousResult<java.util.Set<androidx.health.connect.client.permission.Permission>>? getSynchronousResult(android.content.Context context, java.util.Set<androidx.health.connect.client.permission.Permission> input);
+    method public java.util.Set<androidx.health.connect.client.permission.Permission> parseResult(int resultCode, android.content.Intent? intent);
+  }
+
+  public final class Permission {
+    method public static androidx.health.connect.client.permission.Permission createReadPermission(kotlin.reflect.KClass<? extends androidx.health.connect.client.records.Record> recordType);
+    method public static androidx.health.connect.client.permission.Permission createWritePermission(kotlin.reflect.KClass<? extends androidx.health.connect.client.records.Record> recordType);
+    field public static final androidx.health.connect.client.permission.Permission.Companion Companion;
+  }
+
+  public static final class Permission.Companion {
+    method public androidx.health.connect.client.permission.Permission createReadPermission(kotlin.reflect.KClass<? extends androidx.health.connect.client.records.Record> recordType);
+    method public androidx.health.connect.client.permission.Permission createWritePermission(kotlin.reflect.KClass<? extends androidx.health.connect.client.records.Record> recordType);
+  }
+
+}
+
 package androidx.health.connect.client.records {
 
   public final class ActivityTypes {
@@ -327,6 +357,10 @@
     ctor public ChangesTokenRequest(java.util.Set<? extends kotlin.reflect.KClass<? extends androidx.health.connect.client.records.Record>> recordTypes, optional java.util.Set<androidx.health.connect.client.metadata.DataOrigin> dataOriginFilters);
   }
 
+  public final class ReadRecordsRequest<T extends androidx.health.connect.client.records.Record> {
+    ctor public ReadRecordsRequest(kotlin.reflect.KClass<T> recordType, androidx.health.connect.client.time.TimeRangeFilter timeRangeFilter, optional java.util.List<androidx.health.connect.client.metadata.DataOrigin> dataOriginFilter, optional boolean ascendingOrder, optional int pageSize, optional String? pageToken);
+  }
+
 }
 
 package androidx.health.connect.client.response {
@@ -347,6 +381,13 @@
     property public final java.util.List<java.lang.String> recordUidsList;
   }
 
+  public final class ReadRecordsResponse<T extends androidx.health.connect.client.records.Record> {
+    method public String? getPageToken();
+    method public java.util.List<T> getRecords();
+    property public final String? pageToken;
+    property public final java.util.List<T> records;
+  }
+
 }
 
 package androidx.health.connect.client.time {
diff --git a/health/health-connect-client/api/restricted_current.txt b/health/health-connect-client/api/restricted_current.txt
index 0cc077d..14bd9c5 100644
--- a/health/health-connect-client/api/restricted_current.txt
+++ b/health/health-connect-client/api/restricted_current.txt
@@ -8,9 +8,12 @@
     method public suspend Object? getChangesToken(androidx.health.connect.client.request.ChangesTokenRequest request, kotlin.coroutines.Continuation<? super java.lang.String>);
     method public default static androidx.health.connect.client.HealthConnectClient getOrCreate(android.content.Context context, optional java.util.List<java.lang.String> packageNames);
     method public default static androidx.health.connect.client.HealthConnectClient getOrCreate(android.content.Context context);
+    method public androidx.health.connect.client.PermissionController getPermissionController();
     method public suspend Object? insertRecords(java.util.List<? extends androidx.health.connect.client.records.Record> records, kotlin.coroutines.Continuation<? super androidx.health.connect.client.response.InsertRecordsResponse>);
     method public default static boolean isAvailable(android.content.Context context, optional java.util.List<java.lang.String> packageNames);
     method public default static boolean isAvailable(android.content.Context context);
+    method public suspend <T extends androidx.health.connect.client.records.Record> Object? readRecords(androidx.health.connect.client.request.ReadRecordsRequest<T> request, kotlin.coroutines.Continuation<? super androidx.health.connect.client.response.ReadRecordsResponse<T>>);
+    property public abstract androidx.health.connect.client.PermissionController permissionController;
     field public static final androidx.health.connect.client.HealthConnectClient.Companion Companion;
   }
 
@@ -21,6 +24,11 @@
     method public boolean isAvailable(android.content.Context context);
   }
 
+  public interface PermissionController {
+    method public suspend Object? getGrantedPermissions(java.util.Set<androidx.health.connect.client.permission.Permission> permissions, kotlin.coroutines.Continuation<? super java.util.Set<? extends androidx.health.connect.client.permission.Permission>>);
+    method public suspend Object? revokeAllPermissions(kotlin.coroutines.Continuation<? super kotlin.Unit>);
+  }
+
 }
 
 package androidx.health.connect.client.changes {
@@ -91,6 +99,28 @@
 
 }
 
+package androidx.health.connect.client.permission {
+
+  public final class HealthDataRequestPermissions extends androidx.activity.result.contract.ActivityResultContract<java.util.Set<? extends androidx.health.connect.client.permission.Permission>,java.util.Set<? extends androidx.health.connect.client.permission.Permission>> {
+    ctor public HealthDataRequestPermissions(optional String providerPackageName);
+    method public android.content.Intent createIntent(android.content.Context context, java.util.Set<androidx.health.connect.client.permission.Permission> input);
+    method public androidx.activity.result.contract.ActivityResultContract.SynchronousResult<java.util.Set<androidx.health.connect.client.permission.Permission>>? getSynchronousResult(android.content.Context context, java.util.Set<androidx.health.connect.client.permission.Permission> input);
+    method public java.util.Set<androidx.health.connect.client.permission.Permission> parseResult(int resultCode, android.content.Intent? intent);
+  }
+
+  public final class Permission {
+    method public static androidx.health.connect.client.permission.Permission createReadPermission(kotlin.reflect.KClass<? extends androidx.health.connect.client.records.Record> recordType);
+    method public static androidx.health.connect.client.permission.Permission createWritePermission(kotlin.reflect.KClass<? extends androidx.health.connect.client.records.Record> recordType);
+    field public static final androidx.health.connect.client.permission.Permission.Companion Companion;
+  }
+
+  public static final class Permission.Companion {
+    method public androidx.health.connect.client.permission.Permission createReadPermission(kotlin.reflect.KClass<? extends androidx.health.connect.client.records.Record> recordType);
+    method public androidx.health.connect.client.permission.Permission createWritePermission(kotlin.reflect.KClass<? extends androidx.health.connect.client.records.Record> recordType);
+  }
+
+}
+
 package androidx.health.connect.client.records {
 
   public final class ActivityTypes {
@@ -345,6 +375,10 @@
     ctor public ChangesTokenRequest(java.util.Set<? extends kotlin.reflect.KClass<? extends androidx.health.connect.client.records.Record>> recordTypes, optional java.util.Set<androidx.health.connect.client.metadata.DataOrigin> dataOriginFilters);
   }
 
+  public final class ReadRecordsRequest<T extends androidx.health.connect.client.records.Record> {
+    ctor public ReadRecordsRequest(kotlin.reflect.KClass<T> recordType, androidx.health.connect.client.time.TimeRangeFilter timeRangeFilter, optional java.util.List<androidx.health.connect.client.metadata.DataOrigin> dataOriginFilter, optional boolean ascendingOrder, optional int pageSize, optional String? pageToken);
+  }
+
 }
 
 package androidx.health.connect.client.response {
@@ -365,6 +399,13 @@
     property public final java.util.List<java.lang.String> recordUidsList;
   }
 
+  public final class ReadRecordsResponse<T extends androidx.health.connect.client.records.Record> {
+    method public String? getPageToken();
+    method public java.util.List<T> getRecords();
+    property public final String? pageToken;
+    property public final java.util.List<T> records;
+  }
+
 }
 
 package androidx.health.connect.client.time {
diff --git a/health/health-connect-client/src/main/java/androidx/health/connect/client/HealthConnectClient.kt b/health/health-connect-client/src/main/java/androidx/health/connect/client/HealthConnectClient.kt
index 98c47a3..c0c0b45 100644
--- a/health/health-connect-client/src/main/java/androidx/health/connect/client/HealthConnectClient.kt
+++ b/health/health-connect-client/src/main/java/androidx/health/connect/client/HealthConnectClient.kt
@@ -26,7 +26,6 @@
 import androidx.health.connect.client.aggregate.AggregateMetric
 import androidx.health.connect.client.impl.HealthConnectClientImpl
 import androidx.health.connect.client.metadata.DataOrigin
-import androidx.health.connect.client.permission.Permission
 import androidx.health.connect.client.records.Record
 import androidx.health.connect.client.request.AggregateGroupByDurationRequest
 import androidx.health.connect.client.request.AggregateGroupByPeriodRequest
@@ -44,16 +43,9 @@
 
 /** Interface to access health and fitness records. */
 interface HealthConnectClient {
-    /**
-     * Returns a set of [Permission] granted by the user to this app, out of the input [permissions]
-     * set.
-     *
-     * @throws RemoteException For any IPC transportation failures.
-     * @throws IOException For any disk I/O issues.
-     * @throws IllegalStateException If service is not available.
-     */
-    @RestrictTo(RestrictTo.Scope.LIBRARY)
-    suspend fun getGrantedPermissions(permissions: Set<Permission>): Set<Permission>
+
+    /** Access operations related to permissions. */
+    val permissionController: PermissionController
 
     /**
      * Inserts one or more [Record] and returns newly assigned
@@ -141,7 +133,6 @@
      * @throws IOException For any disk I/O issues.
      * @throws IllegalStateException If service is not available.
      */
-    @RestrictTo(RestrictTo.Scope.LIBRARY)
     suspend fun <T : Record> readRecords(request: ReadRecordsRequest<T>): ReadRecordsResponse<T>
 
     /**
diff --git a/health/health-connect-client/src/main/java/androidx/health/connect/client/PermissionController.kt b/health/health-connect-client/src/main/java/androidx/health/connect/client/PermissionController.kt
new file mode 100644
index 0000000..a9dc0987
--- /dev/null
+++ b/health/health-connect-client/src/main/java/androidx/health/connect/client/PermissionController.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.health.connect.client
+
+import androidx.health.connect.client.permission.Permission
+
+/** Interface for operations related to permissions. */
+interface PermissionController {
+    /**
+     * Returns a set of [Permission] granted by the user to the calling app, out of the input
+     * [permissions] set.
+     *
+     * @param permissions set of permissions interested to check if granted or not
+     * @return set of granted permissions.
+     *
+     * @throws RemoteException For any IPC transportation failures.
+     * @throws IOException For any disk I/O issues.
+     * @throws IllegalStateException If service is not available.
+     */
+    suspend fun getGrantedPermissions(permissions: Set<Permission>): Set<Permission>
+
+    /**
+     * Revokes all previously granted [Permission] by the user to the calling app.
+     *
+     * @throws RemoteException For any IPC transportation failures.
+     * @throws IOException For any disk I/O issues.
+     * @throws IllegalStateException If service is not available.
+     */
+    suspend fun revokeAllPermissions()
+}
diff --git a/health/health-connect-client/src/main/java/androidx/health/connect/client/impl/HealthConnectClientImpl.kt b/health/health-connect-client/src/main/java/androidx/health/connect/client/impl/HealthConnectClientImpl.kt
index 25e449a..71d6c22 100644
--- a/health/health-connect-client/src/main/java/androidx/health/connect/client/impl/HealthConnectClientImpl.kt
+++ b/health/health-connect-client/src/main/java/androidx/health/connect/client/impl/HealthConnectClientImpl.kt
@@ -16,6 +16,7 @@
 package androidx.health.connect.client.impl
 
 import androidx.health.connect.client.HealthConnectClient
+import androidx.health.connect.client.PermissionController
 import androidx.health.connect.client.aggregate.AggregateDataRow
 import androidx.health.connect.client.aggregate.AggregateDataRowGroupByDuration
 import androidx.health.connect.client.aggregate.AggregateDataRowGroupByPeriod
@@ -60,7 +61,7 @@
  */
 class HealthConnectClientImpl(
     private val delegate: HealthDataAsyncClient,
-) : HealthConnectClient {
+) : HealthConnectClient, PermissionController {
 
     override suspend fun getGrantedPermissions(permissions: Set<Permission>): Set<Permission> {
         return delegate
@@ -70,6 +71,13 @@
             .toSet()
     }
 
+    override suspend fun revokeAllPermissions() {
+        return delegate.revokeAllPermissions().await()
+    }
+
+    override val permissionController: PermissionController
+        get() = this
+
     override suspend fun insertRecords(records: List<Record>): InsertRecordsResponse {
         val uidList = delegate.insertData(records.map { it.toProto() }).await()
         return InsertRecordsResponse(recordUidsList = uidList)
diff --git a/health/health-connect-client/src/main/java/androidx/health/connect/client/impl/converters/request/ReadDataRangeRequestToProto.kt b/health/health-connect-client/src/main/java/androidx/health/connect/client/impl/converters/request/ReadDataRangeRequestToProto.kt
index a8d4b74..3b3603e 100644
--- a/health/health-connect-client/src/main/java/androidx/health/connect/client/impl/converters/request/ReadDataRangeRequestToProto.kt
+++ b/health/health-connect-client/src/main/java/androidx/health/connect/client/impl/converters/request/ReadDataRangeRequestToProto.kt
@@ -39,12 +39,7 @@
                 }
             )
             setAscOrdering(request.ascendingOrder)
-            if (request.hasLimit()) {
-                setLimit(request.limit)
-            }
-            if (request.hasPageSize()) {
-                setPageSize(request.pageSize)
-            }
+            setPageSize(request.pageSize)
             request.pageToken?.let { setPageToken(it) }
         }
         .build()
diff --git a/health/health-connect-client/src/main/java/androidx/health/connect/client/permission/AccessTypes.kt b/health/health-connect-client/src/main/java/androidx/health/connect/client/permission/AccessTypes.kt
index f2b9b94..596ac87 100644
--- a/health/health-connect-client/src/main/java/androidx/health/connect/client/permission/AccessTypes.kt
+++ b/health/health-connect-client/src/main/java/androidx/health/connect/client/permission/AccessTypes.kt
@@ -18,7 +18,11 @@
 import androidx.annotation.IntDef
 import androidx.annotation.RestrictTo
 
-/** Type of access to health data: read or write. */
+/**
+ * Type of access to health data: read or write.
+ *
+ * @see Permission.create
+ */
 @RestrictTo(RestrictTo.Scope.LIBRARY)
 public object AccessTypes {
     const val READ = 1
diff --git a/health/health-connect-client/src/main/java/androidx/health/connect/client/permission/HealthDataRequestPermissions.kt b/health/health-connect-client/src/main/java/androidx/health/connect/client/permission/HealthDataRequestPermissions.kt
index c215b9e..a1e1d2f 100644
--- a/health/health-connect-client/src/main/java/androidx/health/connect/client/permission/HealthDataRequestPermissions.kt
+++ b/health/health-connect-client/src/main/java/androidx/health/connect/client/permission/HealthDataRequestPermissions.kt
@@ -18,7 +18,6 @@
 import android.content.Context
 import android.content.Intent
 import androidx.activity.result.contract.ActivityResultContract
-import androidx.annotation.RestrictTo
 import androidx.health.connect.client.HealthConnectClient.Companion.DEFAULT_PROVIDER_PACKAGE_NAME
 import androidx.health.connect.client.impl.converters.permission.toJetpackPermission
 import androidx.health.connect.client.impl.converters.permission.toProtoPermission
@@ -32,9 +31,10 @@
  *
  * @param providerPackageName Optional provider package name for the backing implementation of
  * choice.
+ *
+ * @see androidx.activity.ComponentActivity.registerForActivityResult
  */
-@RestrictTo(RestrictTo.Scope.LIBRARY)
-class HealthDataRequestPermissions(
+public class HealthDataRequestPermissions(
     private val providerPackageName: String = DEFAULT_PROVIDER_PACKAGE_NAME,
 ) : ActivityResultContract<Set<Permission>, Set<Permission>>() {
 
diff --git a/health/health-connect-client/src/main/java/androidx/health/connect/client/permission/Permission.kt b/health/health-connect-client/src/main/java/androidx/health/connect/client/permission/Permission.kt
index bf42e71..6b04daf 100644
--- a/health/health-connect-client/src/main/java/androidx/health/connect/client/permission/Permission.kt
+++ b/health/health-connect-client/src/main/java/androidx/health/connect/client/permission/Permission.kt
@@ -15,32 +15,42 @@
  */
 package androidx.health.connect.client.permission
 
-import androidx.annotation.RestrictTo
 import androidx.health.connect.client.records.Record
 import kotlin.reflect.KClass
 
 /**
- * Class to represent a permission which consists of a [KClass] representing a data type and an
- * access type.
+ * A Permission either to read or write data associated with a [Record] type.
  *
- * @property recordType type of [Record] the permission gives access for
- * @property accessType whether read or write access
+ * @see androidx.health.connect.client.PermissionController
  */
-@RestrictTo(RestrictTo.Scope.LIBRARY)
-public class Permission(
+public class Permission
+internal constructor(
+    /** type of [Record] the permission gives access for. */
     internal val recordType: KClass<out Record>,
+    /** whether read or write access. */
     @property:AccessType internal val accessType: Int,
 ) {
     companion object {
         /**
-         * Creates a permission of the given [accessType] for record type [T].
+         * Creates permission to read provided [recordType], such as `Steps::class`.
          *
-         * @param T type of [Record]
-         * @param accessType whether read or write access
-         * @return Permission for given [accessType] for record type [T]
+         * @return Permission object to use with
+         * [androidx.health.connect.client.PermissionController].
          */
-        public inline fun <reified T : Record> create(@AccessType accessType: Int): Permission {
-            return Permission(T::class, accessType)
+        @JvmStatic
+        public fun createReadPermission(recordType: KClass<out Record>): Permission {
+            return Permission(recordType, AccessTypes.READ)
+        }
+
+        /**
+         * Creates permission to write provided [recordType], such as `Steps::class`.
+         *
+         * @return Permission object to use with
+         * [androidx.health.connect.client.PermissionController].
+         */
+        @JvmStatic
+        public fun createWritePermission(recordType: KClass<out Record>): Permission {
+            return Permission(recordType, AccessTypes.READ)
         }
     }
 
diff --git a/health/health-connect-client/src/main/java/androidx/health/connect/client/permission/package-info.java b/health/health-connect-client/src/main/java/androidx/health/connect/client/permission/package-info.java
deleted file mode 100644
index 7fa824d..0000000
--- a/health/health-connect-client/src/main/java/androidx/health/connect/client/permission/package-info.java
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/** @hide */
-@RestrictTo(RestrictTo.Scope.LIBRARY)
-package androidx.health.data.client.permission;
-
-import androidx.annotation.RestrictTo;
diff --git a/health/health-connect-client/src/main/java/androidx/health/connect/client/request/ReadRecordsRequest.kt b/health/health-connect-client/src/main/java/androidx/health/connect/client/request/ReadRecordsRequest.kt
index 7c7314f..d2fd573 100644
--- a/health/health-connect-client/src/main/java/androidx/health/connect/client/request/ReadRecordsRequest.kt
+++ b/health/health-connect-client/src/main/java/androidx/health/connect/client/request/ReadRecordsRequest.kt
@@ -15,7 +15,6 @@
  */
 package androidx.health.connect.client.request
 
-import androidx.annotation.RestrictTo
 import androidx.health.connect.client.metadata.DataOrigin
 import androidx.health.connect.client.records.Record
 import androidx.health.connect.client.time.TimeRangeFilter
@@ -25,43 +24,61 @@
  * Request object to read [Record]s in Android Health Platform determined by time range and other
  * filters.
  *
- * @property recordType Which type of [Record] to read, such as `Steps::class`.
- * @property timeRangeFilter The [TimeRangeFilter] to read from. If open-ended in any direction,
- * [limit] or [pageSize] must be set.
- * @property dataOriginFilter List of [DataOrigin] to read from, or empty for no filter.
- * @property ascendingOrder Whether the [Record] should be returned in ascending or descending order
- * by time. Default is true.
- * @property limit Maximum number of [Record] to read. Cannot be set together with [pageSize]. Must
- * be set if [timeRangeFilter] is open-ended in any direction.
- * @property pageSize Maximum number of [Record] within one page. If there's more data remaining
- * (and the next page should be read), the response will contain a [pageToken] to be used in the
- * subsequent read request. Cannot be set together with [limit].
- * @property pageToken Continuation token to access the next page, returned in the response to the
- * previous page read request. [pageSize] must be set together with [pageToken].
+ * Returned collection will contain a
+ * [androidx.health.data.client.response.ReadRecordsResponse.pageToken] if number of records exceeds
+ * [pageSize]. Use this if you expect an unbound number of records within specified time ranges.
+ * Stops at any time once desired amount of records are processed.
+ *
+ * @param T type of [Record], such as `Steps`.
+ * @param recordType Which type of [Record] to read, such as `Steps::class`.
+ * @param timeRangeFilter The [TimeRangeFilter] to read from.
+ * @param dataOriginFilter List of [DataOrigin] to read from, or empty for no filter.
+ * @param ascendingOrder Whether the [Record] should be returned in ascending or descending order by
+ * time. Default is true for ascending.
+ * @param pageSize Maximum number of [Record] within one page. If there's more data remaining (and
+ * the next page should be read), the response will contain a [pageToken] to be used in the
+ * subsequent read request. Must be positive, default to 1000.
+ * @param pageToken Continuation token to access the next page, returned in the response to the
+ * previous page read request, or `null` for the initial request for the first page.
+ *
+ * @see androidx.health.connect.client.response.ReadRecordsResponse
+ * @see androidx.health.connect.client.HealthConnectClient.readRecords
  */
-@RestrictTo(RestrictTo.Scope.LIBRARY)
-class ReadRecordsRequest<T : Record>(
-    val recordType: KClass<T>,
-    val timeRangeFilter: TimeRangeFilter,
-    val dataOriginFilter: List<DataOrigin> = emptyList(),
-    val ascendingOrder: Boolean = true,
-    val limit: Int = 0,
-    val pageSize: Int = 0,
-    val pageToken: String? = null
+public class ReadRecordsRequest<T : Record>(
+    internal val recordType: KClass<T>,
+    internal val timeRangeFilter: TimeRangeFilter,
+    internal val dataOriginFilter: List<DataOrigin> = emptyList(),
+    internal val ascendingOrder: Boolean = true,
+    internal val pageSize: Int = 1000,
+    internal val pageToken: String? = null,
 ) {
     init {
-        require(limit == 0 || pageSize == 0) { "pageSize and limit can't be used at the same time" }
-        if (timeRangeFilter.isOpenEnded()) {
-            require(limit > 0 || pageSize > 0) {
-                "When timeRangeFilter is open-ended, either limit or pageSize must be set"
-            }
-        }
-        if (pageToken != null) {
-            require(pageSize > 0) { "pageToken must be set with pageSize" }
-        }
+        require(pageSize > 0) { "pageSize must be positive." }
     }
 
-    internal fun hasLimit(): Boolean = limit > 0
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (javaClass != other?.javaClass) return false
 
-    internal fun hasPageSize(): Boolean = pageSize > 0
+        other as ReadRecordsRequest<*>
+
+        if (recordType != other.recordType) return false
+        if (timeRangeFilter != other.timeRangeFilter) return false
+        if (dataOriginFilter != other.dataOriginFilter) return false
+        if (ascendingOrder != other.ascendingOrder) return false
+        if (pageSize != other.pageSize) return false
+        if (pageToken != other.pageToken) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = recordType.hashCode()
+        result = 31 * result + timeRangeFilter.hashCode()
+        result = 31 * result + dataOriginFilter.hashCode()
+        result = 31 * result + ascendingOrder.hashCode()
+        result = 31 * result + pageSize
+        result = 31 * result + (pageToken?.hashCode() ?: 0)
+        return result
+    }
 }
diff --git a/health/health-connect-client/src/main/java/androidx/health/connect/client/response/ReadRecordsResponse.kt b/health/health-connect-client/src/main/java/androidx/health/connect/client/response/ReadRecordsResponse.kt
index 0565e97..a86b75b 100644
--- a/health/health-connect-client/src/main/java/androidx/health/connect/client/response/ReadRecordsResponse.kt
+++ b/health/health-connect-client/src/main/java/androidx/health/connect/client/response/ReadRecordsResponse.kt
@@ -15,14 +15,18 @@
  */
 package androidx.health.connect.client.response
 
-import androidx.annotation.RestrictTo
 import androidx.health.connect.client.records.Record
 
 /**
- * Response to records read.
+ * Response of reading a collection of records.
  *
- * @see [HealthConnectClient.readRecords]
+ * @param T the record type
+ * @property records a collection of records
+ * @property pageToken an optional page token to use for
+ * [androidx.health.connect.client.request.ReadRecordsRequest.pageToken] in the next request if more
+ * records can be fetched; contains value `null` if no more pages.
+ *
+ * @see androidx.health.connect.client.HealthConnectClient.readRecords
  */
-@RestrictTo(RestrictTo.Scope.LIBRARY)
 class ReadRecordsResponse<T : Record>
 internal constructor(val records: List<T>, val pageToken: String?)
diff --git a/health/health-connect-client/src/test/java/androidx/health/connect/client/impl/HealthConnectClientImplTest.kt b/health/health-connect-client/src/test/java/androidx/health/connect/client/impl/HealthConnectClientImplTest.kt
index c0c5fae..c9adb81 100644
--- a/health/health-connect-client/src/test/java/androidx/health/connect/client/impl/HealthConnectClientImplTest.kt
+++ b/health/health-connect-client/src/test/java/androidx/health/connect/client/impl/HealthConnectClientImplTest.kt
@@ -22,7 +22,6 @@
 import android.content.pm.ApplicationInfo
 import android.content.pm.PackageInfo
 import android.os.Looper
-import androidx.health.connect.client.HealthConnectClient
 import androidx.health.connect.client.aggregate.AggregateDataRow
 import androidx.health.connect.client.aggregate.AggregateDataRowGroupByDuration
 import androidx.health.connect.client.aggregate.AggregateDataRowGroupByPeriod
@@ -31,7 +30,6 @@
 import androidx.health.connect.client.metadata.DataOrigin
 import androidx.health.connect.client.metadata.Device
 import androidx.health.connect.client.metadata.Metadata
-import androidx.health.connect.client.permission.AccessTypes
 import androidx.health.connect.client.permission.Permission
 import androidx.health.connect.client.records.ActiveCaloriesBurned
 import androidx.health.connect.client.records.Nutrition
@@ -86,8 +84,9 @@
 private const val PROVIDER_PACKAGE_NAME = "com.google.fake.provider"
 
 private val API_METHOD_LIST =
-    listOf<suspend HealthConnectClient.() -> Unit>(
+    listOf<suspend HealthConnectClientImpl.() -> Unit>(
         { getGrantedPermissions(setOf()) },
+        { revokeAllPermissions() },
         { insertRecords(listOf()) },
         { updateRecords(listOf()) },
         { deleteRecords(ActiveCaloriesBurned::class, listOf(), listOf()) },
@@ -100,7 +99,7 @@
                     TimeRangeFilter.between(
                         Instant.ofEpochMilli(1234L),
                         Instant.ofEpochMilli(1235L)
-                    )
+                    ),
                 )
             )
         },
@@ -184,7 +183,7 @@
     fun getGrantedPermissions_none() = runTest {
         val deferred = async {
             healthConnectClient.getGrantedPermissions(
-                setOf(Permission.create<Steps>(AccessTypes.READ))
+                setOf(Permission.createReadPermission(Steps::class))
             )
         }
 
@@ -207,7 +206,7 @@
         )
         val deferred = async {
             healthConnectClient.getGrantedPermissions(
-                setOf(Permission.create<Steps>(AccessTypes.READ))
+                setOf(Permission.createReadPermission(Steps::class))
             )
         }
 
@@ -215,7 +214,7 @@
         waitForMainLooperIdle()
 
         val response = deferred.await()
-        assertThat(response).containsExactly(Permission.create<Steps>(AccessTypes.READ))
+        assertThat(response).containsExactly(Permission.createReadPermission(Steps::class))
     }
 
     @Test
@@ -396,7 +395,7 @@
                 ReadRecordsRequest(
                     Steps::class,
                     timeRangeFilter = TimeRangeFilter.before(endTime = Instant.ofEpochMilli(7890L)),
-                    limit = 10
+                    pageSize = 10
                 )
             )
         }
@@ -411,7 +410,7 @@
                     .setTimeSpec(TimeProto.TimeSpec.newBuilder().setEndTimeEpochMs(7890L))
                     .setDataType(DataProto.DataType.newBuilder().setName("Steps"))
                     .setAscOrdering(true)
-                    .setLimit(10)
+                    .setPageSize(10)
                     .build()
             )
         assertThat(response.pageToken).isEqualTo("nextPageToken")
diff --git a/health/health-connect-client/src/test/java/androidx/health/connect/client/permission/HealthDataRequestPermissionsTest.kt b/health/health-connect-client/src/test/java/androidx/health/connect/client/permission/HealthDataRequestPermissionsTest.kt
index 33b708c..3d2bde2 100644
--- a/health/health-connect-client/src/test/java/androidx/health/connect/client/permission/HealthDataRequestPermissionsTest.kt
+++ b/health/health-connect-client/src/test/java/androidx/health/connect/client/permission/HealthDataRequestPermissionsTest.kt
@@ -47,7 +47,7 @@
         val intent =
             requestPermissionContract.createIntent(
                 context,
-                setOf(Permission.create<Steps>(AccessTypes.READ))
+                setOf(Permission.createReadPermission(Steps::class))
             )
 
         assertThat(intent.action).isEqualTo("androidx.health.ACTION_REQUEST_PERMISSIONS")
@@ -60,7 +60,7 @@
         val intent =
             requestPermissionContract.createIntent(
                 context,
-                setOf(Permission.create<Steps>(AccessTypes.READ))
+                setOf(Permission.createReadPermission(Steps::class))
             )
 
         assertThat(intent.action).isEqualTo("androidx.health.ACTION_REQUEST_PERMISSIONS")
@@ -100,7 +100,7 @@
         )
         val result = requestPermissionContract.parseResult(0, intent)
 
-        assertThat(result).containsExactly(Permission.create<Steps>(AccessTypes.READ))
+        assertThat(result).containsExactly(Permission.createReadPermission(Steps::class))
     }
 
     @Test
@@ -109,7 +109,7 @@
         val result =
             requestPermissionContract.getSynchronousResult(
                 context,
-                setOf(Permission.create<Steps>(AccessTypes.READ))
+                setOf(Permission.createReadPermission(Steps::class))
             )
 
         assertThat(result).isNull()
diff --git a/health/health-connect-client/src/test/java/androidx/health/connect/client/request/ReadRecordsRequestTest.kt b/health/health-connect-client/src/test/java/androidx/health/connect/client/request/ReadRecordsRequestTest.kt
index 2a3bd0c..c9628d1 100644
--- a/health/health-connect-client/src/test/java/androidx/health/connect/client/request/ReadRecordsRequestTest.kt
+++ b/health/health-connect-client/src/test/java/androidx/health/connect/client/request/ReadRecordsRequestTest.kt
@@ -19,8 +19,7 @@
 import androidx.health.connect.client.time.TimeRangeFilter
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import java.time.Instant
-import org.junit.Assert.assertEquals
-import org.junit.Assert.assertThrows
+import kotlin.test.assertFailsWith
 import org.junit.Test
 import org.junit.runner.RunWith
 
@@ -31,49 +30,32 @@
         TimeRangeFilter.between(Instant.ofEpochMilli(1234L), Instant.ofEpochMilli(1235L))
 
     @Test
-    fun limitAndSizeTogether_throws() {
-        val exception =
-            assertThrows(IllegalArgumentException::class.java) {
-                ReadRecordsRequest(
-                    recordType = Steps::class,
-                    timeRangeFilter = TimeRangeFilter.none(),
-                    limit = 10,
-                    pageSize = 10
-                )
-            }
-        assertEquals("pageSize and limit can't be used at the same time", exception.message)
+    fun negativePageSize_throws() {
+        assertFailsWith<IllegalArgumentException> {
+            ReadRecordsRequest(
+                recordType = Steps::class,
+                timeRangeFilter = TimeRangeFilter.none(),
+                pageSize = -1
+            )
+        }
     }
 
     @Test
-    fun openEndedTimeRange_withoutLimitOrPageSize_throws() {
-        val exception =
-            assertThrows(IllegalArgumentException::class.java) {
-                ReadRecordsRequest(
-                    recordType = Steps::class,
-                    timeRangeFilter = TimeRangeFilter.none()
-                )
-            }
-        assertEquals(
-            "When timeRangeFilter is open-ended, either limit or pageSize must be set",
-            exception.message
-        )
+    fun zeroPageSize_throws() {
+        assertFailsWith<IllegalArgumentException> {
+            ReadRecordsRequest(
+                recordType = Steps::class,
+                timeRangeFilter = TimeRangeFilter.none(),
+                pageSize = 0
+            )
+        }
     }
 
     @Test
-    fun openEndedTimeRange_withLimit_success() {
+    fun openEndedTimeRange_success() {
         ReadRecordsRequest(
             recordType = Steps::class,
             timeRangeFilter = TimeRangeFilter.none(),
-            limit = 10
-        )
-    }
-
-    @Test
-    fun openEndedTimeRange_withPageSize_success() {
-        ReadRecordsRequest(
-            recordType = Steps::class,
-            timeRangeFilter = TimeRangeFilter.none(),
-            pageSize = 10
         )
     }
 
@@ -83,19 +65,6 @@
     }
 
     @Test
-    fun pageTokenWithoutPageSize_throws() {
-        val exception =
-            assertThrows(IllegalArgumentException::class.java) {
-                ReadRecordsRequest(
-                    recordType = Steps::class,
-                    timeRangeFilter = closedTimeRange,
-                    pageToken = "token"
-                )
-            }
-        assertEquals("pageToken must be set with pageSize", exception.message)
-    }
-
-    @Test
     fun pageTokenWithPageSize_success() {
         ReadRecordsRequest(
             recordType = Steps::class,
diff --git a/health/health-connect-client/src/test/java/androidx/health/platform/client/impl/testing/FakeHealthDataService.kt b/health/health-connect-client/src/test/java/androidx/health/platform/client/impl/testing/FakeHealthDataService.kt
index 864aa1f..a119d78 100644
--- a/health/health-connect-client/src/test/java/androidx/health/platform/client/impl/testing/FakeHealthDataService.kt
+++ b/health/health-connect-client/src/test/java/androidx/health/platform/client/impl/testing/FakeHealthDataService.kt
@@ -96,6 +96,10 @@
         context: RequestContext,
         callback: IRevokeAllPermissionsCallback,
     ) {
+        errorCode?.let {
+            callback.onError(ErrorStatus.create(it, "" + it))
+            return@revokeAllPermissions
+        }
         grantedPermissions.clear()
         callback.onSuccess()
     }
diff --git a/lifecycle/lifecycle-livedata/src/main/java/androidx/lifecycle/MediatorLiveData.java b/lifecycle/lifecycle-livedata/src/main/java/androidx/lifecycle/MediatorLiveData.java
index 58261dd..f9fc4f2 100644
--- a/lifecycle/lifecycle-livedata/src/main/java/androidx/lifecycle/MediatorLiveData.java
+++ b/lifecycle/lifecycle-livedata/src/main/java/androidx/lifecycle/MediatorLiveData.java
@@ -83,6 +83,9 @@
      */
     @MainThread
     public <S> void addSource(@NonNull LiveData<S> source, @NonNull Observer<? super S> onChanged) {
+        if (source == null) {
+            throw new NullPointerException("source cannot be null");
+        }
         Source<S> e = new Source<>(source, onChanged);
         Source<?> existing = mSources.putIfAbsent(source, e);
         if (existing != null && existing.mObserver != onChanged) {
diff --git a/lifecycle/lifecycle-livedata/src/test/java/androidx/lifecycle/MediatorLiveDataTest.java b/lifecycle/lifecycle-livedata/src/test/java/androidx/lifecycle/MediatorLiveDataTest.java
index 24abb51..eacd430 100644
--- a/lifecycle/lifecycle-livedata/src/test/java/androidx/lifecycle/MediatorLiveDataTest.java
+++ b/lifecycle/lifecycle-livedata/src/test/java/androidx/lifecycle/MediatorLiveDataTest.java
@@ -35,6 +35,7 @@
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.ExpectedException;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
@@ -44,6 +45,9 @@
 
     @Rule
     public InstantTaskExecutorRule mInstantTaskExecutorRule = new InstantTaskExecutorRule();
+    @Rule
+    @SuppressWarnings("deprecation")
+    public ExpectedException exception = ExpectedException.none();
 
     private TestLifecycleOwner mOwner;
     private MediatorLiveData<String> mMediator;
@@ -239,4 +243,14 @@
         assertThat(mMediator.getValue(), is("c"));
     }
 
+    @Test
+    public void addNullSource() {
+        Observer observer = mock(Observer.class);
+        exception.expect(NullPointerException.class);
+        exception.expectMessage("source cannot be null");
+
+        mMediator.addSource(null, observer);
+
+    }
+
 }
diff --git a/playground-common/README.md b/playground-common/README.md
index 4b0258ac..af45e5a 100644
--- a/playground-common/README.md
+++ b/playground-common/README.md
@@ -56,7 +56,7 @@
 shared properties are kept in `androidx-shared.properties` file.
 The dynamic properties are read in the `playground` plugin and set on each project.
 
-There is a `VerifyPlaygroundGradlePropertiesTask` task that validates the contents of
+There is a `VerifyPlaygroundGradleConfigurationTask` task that validates the contents of
 `androidx-shared.properties` file as part of the main AndroidX build.
 
 ### Optional Dependencies
diff --git a/playground-common/androidx-shared.properties b/playground-common/androidx-shared.properties
index 13e99b2b..c31e4d0 100644
--- a/playground-common/androidx-shared.properties
+++ b/playground-common/androidx-shared.properties
@@ -33,6 +33,7 @@
 android.defaults.buildfeatures.resvalues=false
 android.defaults.buildfeatures.shaders=false
 android.disableAutomaticComponentCreation=true
+android.experimental.lint.missingBaselineIsEmptyBaseline=true
 
 org.gradle.configureondemand=true
 org.gradle.parallel=true
diff --git a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/dao/MusicDao.java b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/dao/MusicDao.java
index 8e64440..6f74a64 100644
--- a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/dao/MusicDao.java
+++ b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/dao/MusicDao.java
@@ -208,12 +208,12 @@
     Map<Integer, List<Song>> getReleaseYearToAlbums();
 
     @RewriteQueriesToDropUnusedColumns
-    @MapInfo(keyColumn = "mArtistId")
+    @MapInfo(keyColumn = "mImageYear")
     @Query("SELECT * FROM Artist JOIN Image ON Artist.mArtistName = Image.mArtistInImage")
     LongSparseArray<Artist> getAllAlbumCoverYearToArtistsWithLongSparseArray();
 
     @RewriteQueriesToDropUnusedColumns
-    @MapInfo(keyColumn = "mArtistId")
+    @MapInfo(keyColumn = "mImageYear")
     @Query("SELECT * FROM Artist JOIN Image ON Artist.mArtistName = Image.mArtistInImage")
     SparseArrayCompat<Artist> getAllAlbumCoverYearToArtistsWithIntSparseArray();
 
diff --git a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/test/MultimapQueryTest.java b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/test/MultimapQueryTest.java
index 43130e0..d045d02 100644
--- a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/test/MultimapQueryTest.java
+++ b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/test/MultimapQueryTest.java
@@ -964,7 +964,7 @@
                 mMusicDao.getAllAlbumCoverYearToArtistsWithLongSparseArray();
 
         assertThat(imageToArtistsMap.size()).isEqualTo(2);
-        assertThat(imageToArtistsMap.get(1)).isEqualTo(mRhcp);
+        assertThat(imageToArtistsMap.get(2006L)).isEqualTo(mRhcp);
     }
 
     @Test
@@ -976,8 +976,8 @@
                 mMusicDao.getAllAlbumCoverYearToArtistsWithIntSparseArray();
 
         assertThat(imageToArtistsMap.size()).isEqualTo(2);
-        assertThat(imageToArtistsMap.get(1)).isEqualTo(mRhcp);
-        assertThat(imageToArtistsMap.get(4)).isEqualTo(mPinkFloyd);
+        assertThat(imageToArtistsMap.get(2006)).isEqualTo(mRhcp);
+        assertThat(imageToArtistsMap.get(1973)).isEqualTo(mPinkFloyd);
     }
 
     @Test
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/TypeAdapterStore.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/TypeAdapterStore.kt
index d8d51bf..bd8e0f7 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/TypeAdapterStore.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/TypeAdapterStore.kt
@@ -786,6 +786,15 @@
             }
             return null
         } else {
+            if (columnName != null) {
+                val singleNamedColumn = findCursorValueReader(
+                    typeMirror,
+                    query.resultInfo?.columns?.find { it.name == columnName }?.type
+                )
+                if (singleNamedColumn != null) {
+                    return SingleNamedColumnRowAdapter(singleNamedColumn, columnName)
+                }
+            }
             val singleColumn = findCursorValueReader(typeMirror, null) ?: return null
             return SingleColumnRowAdapter(singleColumn)
         }
diff --git a/savedstate/savedstate/src/main/java/androidx/savedstate/SavedStateRegistryOwner.kt b/savedstate/savedstate/src/main/java/androidx/savedstate/SavedStateRegistryOwner.kt
index 6bb9586..dc63d7f 100644
--- a/savedstate/savedstate/src/main/java/androidx/savedstate/SavedStateRegistryOwner.kt
+++ b/savedstate/savedstate/src/main/java/androidx/savedstate/SavedStateRegistryOwner.kt
@@ -15,12 +15,33 @@
  */
 package androidx.savedstate
 
+import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.LifecycleOwner
-
+import androidx.lifecycle.LifecycleObserver
 /**
  * A scope that owns [SavedStateRegistry]
  *
- * @see ViewTreeSavedStateRegistryOwner
+ * This owner should be passed in to create a [SavedStateRegistryController] object
+ * through which this owner can access and perform operations via the
+ * controller's [SavedStateRegistry]
+ *
+ * [SavedStateRegistryController.performAttach] must be called once (and only once) on the
+ * main thread during the owner's [Lifecycle.State.INITIALIZED] state.
+ * It should be called before you call [SavedStateRegistryController.performRestore]
+ *
+ * [SavedStateRegistryController.performRestore] can be called with a nullable if nothing
+ * needs to be restored, or with the state Bundle to be restored. performRestore can be called
+ * in one of two places:
+ * 1. Directly before the Lifecycle moves to [Lifecycle.State.CREATED]
+ * 2. Before [Lifecycle.State.STARTED] is reached, as part of the [LifecycleObserver]
+ * that is added during owner initialization
+ *
+ * [SavedStateRegistryController.performSave] should be called after owner has been stopped but
+ * before it reaches [Lifecycle.State.DESTROYED] state. Hence it should only be called once the
+ * owner has received the [Lifecycle.Event.ON_STOP] event. The bundle passed to performSave
+ * will be the bundle restored by performRestore.
+ *
+ * @see [ViewTreeSavedStateRegistryOwner]
  */
 interface SavedStateRegistryOwner : LifecycleOwner {
     /**
diff --git a/settings.gradle b/settings.gradle
index 8cae153..f582cf6 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -86,6 +86,11 @@
 dependencyResolutionManagement {
     versionCatalogs {
         libs {
+            def metalavaOverride = System.getenv("METALAVA_VERSION")
+            if (metalavaOverride != null) {
+                logger.warn("Using custom version ${metalavaOverride} of metalava due to METALAVA_VERSION being set.")
+                version('metalava', metalavaOverride)
+            }
             def skikoOverride = System.getenv("SKIKO_VERSION")
             if (skikoOverride != null) {
                 logger.warn("Using custom version ${skikoOverride} of SKIKO due to SKIKO_VERSION being set.")
diff --git a/wear/compose/compose-material/api/current.txt b/wear/compose/compose-material/api/current.txt
index 01ec528..ab115fb 100644
--- a/wear/compose/compose-material/api/current.txt
+++ b/wear/compose/compose-material/api/current.txt
@@ -14,17 +14,23 @@
     method @androidx.compose.runtime.Composable public androidx.wear.compose.material.ButtonColors buttonColors(optional long backgroundColor, optional long contentColor, optional long disabledBackgroundColor, optional long disabledContentColor);
     method public float getCompactButtonBackgroundPadding();
     method public float getDefaultButtonSize();
+    method public float getDefaultIconSize();
     method public float getExtraSmallButtonSize();
     method public float getLargeButtonSize();
+    method public float getLargeIconSize();
     method public float getSmallButtonSize();
+    method public float getSmallIconSize();
     method @androidx.compose.runtime.Composable public androidx.wear.compose.material.ButtonColors iconButtonColors(optional long contentColor);
     method @androidx.compose.runtime.Composable public androidx.wear.compose.material.ButtonColors primaryButtonColors(optional long backgroundColor, optional long contentColor);
     method @androidx.compose.runtime.Composable public androidx.wear.compose.material.ButtonColors secondaryButtonColors(optional long backgroundColor, optional long contentColor);
     property public final float CompactButtonBackgroundPadding;
     property public final float DefaultButtonSize;
+    property public final float DefaultIconSize;
     property public final float ExtraSmallButtonSize;
     property public final float LargeButtonSize;
+    property public final float LargeIconSize;
     property public final float SmallButtonSize;
+    property public final float SmallIconSize;
     field public static final androidx.wear.compose.material.ButtonDefaults INSTANCE;
   }
 
@@ -570,10 +576,14 @@
   }
 
   public final class ToggleButtonDefaults {
+    method public float getDefaultIconSize();
     method public float getDefaultToggleButtonSize();
+    method public float getSmallIconSize();
     method public float getSmallToggleButtonSize();
     method @androidx.compose.runtime.Composable public androidx.wear.compose.material.ToggleButtonColors toggleButtonColors(optional long checkedBackgroundColor, optional long checkedContentColor, optional long disabledCheckedBackgroundColor, optional long disabledCheckedContentColor, optional long uncheckedBackgroundColor, optional long uncheckedContentColor, optional long disabledUncheckedBackgroundColor, optional long disabledUncheckedContentColor);
+    property public final float DefaultIconSize;
     property public final float DefaultToggleButtonSize;
+    property public final float SmallIconSize;
     property public final float SmallToggleButtonSize;
     field public static final androidx.wear.compose.material.ToggleButtonDefaults INSTANCE;
   }
diff --git a/wear/compose/compose-material/api/public_plus_experimental_current.txt b/wear/compose/compose-material/api/public_plus_experimental_current.txt
index 0d98e8b..38eba7f 100644
--- a/wear/compose/compose-material/api/public_plus_experimental_current.txt
+++ b/wear/compose/compose-material/api/public_plus_experimental_current.txt
@@ -14,17 +14,23 @@
     method @androidx.compose.runtime.Composable public androidx.wear.compose.material.ButtonColors buttonColors(optional long backgroundColor, optional long contentColor, optional long disabledBackgroundColor, optional long disabledContentColor);
     method public float getCompactButtonBackgroundPadding();
     method public float getDefaultButtonSize();
+    method public float getDefaultIconSize();
     method public float getExtraSmallButtonSize();
     method public float getLargeButtonSize();
+    method public float getLargeIconSize();
     method public float getSmallButtonSize();
+    method public float getSmallIconSize();
     method @androidx.compose.runtime.Composable public androidx.wear.compose.material.ButtonColors iconButtonColors(optional long contentColor);
     method @androidx.compose.runtime.Composable public androidx.wear.compose.material.ButtonColors primaryButtonColors(optional long backgroundColor, optional long contentColor);
     method @androidx.compose.runtime.Composable public androidx.wear.compose.material.ButtonColors secondaryButtonColors(optional long backgroundColor, optional long contentColor);
     property public final float CompactButtonBackgroundPadding;
     property public final float DefaultButtonSize;
+    property public final float DefaultIconSize;
     property public final float ExtraSmallButtonSize;
     property public final float LargeButtonSize;
+    property public final float LargeIconSize;
     property public final float SmallButtonSize;
+    property public final float SmallIconSize;
     field public static final androidx.wear.compose.material.ButtonDefaults INSTANCE;
   }
 
@@ -649,10 +655,14 @@
   }
 
   public final class ToggleButtonDefaults {
+    method public float getDefaultIconSize();
     method public float getDefaultToggleButtonSize();
+    method public float getSmallIconSize();
     method public float getSmallToggleButtonSize();
     method @androidx.compose.runtime.Composable public androidx.wear.compose.material.ToggleButtonColors toggleButtonColors(optional long checkedBackgroundColor, optional long checkedContentColor, optional long disabledCheckedBackgroundColor, optional long disabledCheckedContentColor, optional long uncheckedBackgroundColor, optional long uncheckedContentColor, optional long disabledUncheckedBackgroundColor, optional long disabledUncheckedContentColor);
+    property public final float DefaultIconSize;
     property public final float DefaultToggleButtonSize;
+    property public final float SmallIconSize;
     property public final float SmallToggleButtonSize;
     field public static final androidx.wear.compose.material.ToggleButtonDefaults INSTANCE;
   }
diff --git a/wear/compose/compose-material/api/restricted_current.txt b/wear/compose/compose-material/api/restricted_current.txt
index 01ec528..ab115fb 100644
--- a/wear/compose/compose-material/api/restricted_current.txt
+++ b/wear/compose/compose-material/api/restricted_current.txt
@@ -14,17 +14,23 @@
     method @androidx.compose.runtime.Composable public androidx.wear.compose.material.ButtonColors buttonColors(optional long backgroundColor, optional long contentColor, optional long disabledBackgroundColor, optional long disabledContentColor);
     method public float getCompactButtonBackgroundPadding();
     method public float getDefaultButtonSize();
+    method public float getDefaultIconSize();
     method public float getExtraSmallButtonSize();
     method public float getLargeButtonSize();
+    method public float getLargeIconSize();
     method public float getSmallButtonSize();
+    method public float getSmallIconSize();
     method @androidx.compose.runtime.Composable public androidx.wear.compose.material.ButtonColors iconButtonColors(optional long contentColor);
     method @androidx.compose.runtime.Composable public androidx.wear.compose.material.ButtonColors primaryButtonColors(optional long backgroundColor, optional long contentColor);
     method @androidx.compose.runtime.Composable public androidx.wear.compose.material.ButtonColors secondaryButtonColors(optional long backgroundColor, optional long contentColor);
     property public final float CompactButtonBackgroundPadding;
     property public final float DefaultButtonSize;
+    property public final float DefaultIconSize;
     property public final float ExtraSmallButtonSize;
     property public final float LargeButtonSize;
+    property public final float LargeIconSize;
     property public final float SmallButtonSize;
+    property public final float SmallIconSize;
     field public static final androidx.wear.compose.material.ButtonDefaults INSTANCE;
   }
 
@@ -570,10 +576,14 @@
   }
 
   public final class ToggleButtonDefaults {
+    method public float getDefaultIconSize();
     method public float getDefaultToggleButtonSize();
+    method public float getSmallIconSize();
     method public float getSmallToggleButtonSize();
     method @androidx.compose.runtime.Composable public androidx.wear.compose.material.ToggleButtonColors toggleButtonColors(optional long checkedBackgroundColor, optional long checkedContentColor, optional long disabledCheckedBackgroundColor, optional long disabledCheckedContentColor, optional long uncheckedBackgroundColor, optional long uncheckedContentColor, optional long disabledUncheckedBackgroundColor, optional long disabledUncheckedContentColor);
+    property public final float DefaultIconSize;
     property public final float DefaultToggleButtonSize;
+    property public final float SmallIconSize;
     property public final float SmallToggleButtonSize;
     field public static final androidx.wear.compose.material.ToggleButtonDefaults INSTANCE;
   }
diff --git a/wear/compose/compose-material/samples/src/main/java/androidx/wear/compose/material/samples/ButtonSample.kt b/wear/compose/compose-material/samples/src/main/java/androidx/wear/compose/material/samples/ButtonSample.kt
index 727a20c..45999be 100644
--- a/wear/compose/compose-material/samples/src/main/java/androidx/wear/compose/material/samples/ButtonSample.kt
+++ b/wear/compose/compose-material/samples/src/main/java/androidx/wear/compose/material/samples/ButtonSample.kt
@@ -23,7 +23,6 @@
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.res.painterResource
-import androidx.compose.ui.unit.dp
 import androidx.wear.compose.material.Button
 import androidx.wear.compose.material.ButtonDefaults
 import androidx.wear.compose.material.CompactButton
@@ -40,7 +39,25 @@
         Icon(
             painter = painterResource(id = R.drawable.ic_airplanemode_active_24px),
             contentDescription = "airplane",
-            modifier = Modifier.size(24.dp).wrapContentSize(align = Alignment.Center),
+            modifier = Modifier
+                .size(ButtonDefaults.DefaultIconSize).wrapContentSize(align = Alignment.Center),
+        )
+    }
+}
+
+@Sampled
+@Composable
+fun LargeButtonWithIcon() {
+    Button(
+        onClick = { /* Do something */ },
+        enabled = true,
+        modifier = Modifier.size(ButtonDefaults.LargeButtonSize)
+    ) {
+        Icon(
+            painter = painterResource(id = R.drawable.ic_airplanemode_active_24px),
+            contentDescription = "airplane",
+            modifier = Modifier
+                .size(ButtonDefaults.LargeIconSize).wrapContentSize(align = Alignment.Center),
         )
     }
 }
@@ -67,7 +84,8 @@
         Icon(
             painter = painterResource(id = R.drawable.ic_airplanemode_active_24px),
             contentDescription = "airplane",
-            modifier = Modifier.size(24.dp).wrapContentSize(align = Alignment.Center),
+            modifier = Modifier
+                .size(ButtonDefaults.SmallIconSize).wrapContentSize(align = Alignment.Center),
         )
     }
 }
diff --git a/wear/compose/compose-material/samples/src/main/java/androidx/wear/compose/material/samples/ToggleButtonSample.kt b/wear/compose/compose-material/samples/src/main/java/androidx/wear/compose/material/samples/ToggleButtonSample.kt
index 1be38e2..d7c8c10 100644
--- a/wear/compose/compose-material/samples/src/main/java/androidx/wear/compose/material/samples/ToggleButtonSample.kt
+++ b/wear/compose/compose-material/samples/src/main/java/androidx/wear/compose/material/samples/ToggleButtonSample.kt
@@ -27,9 +27,9 @@
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.res.painterResource
-import androidx.compose.ui.unit.dp
 import androidx.wear.compose.material.Icon
 import androidx.wear.compose.material.ToggleButton
+import androidx.wear.compose.material.ToggleButtonDefaults
 
 @Sampled
 @Composable
@@ -43,7 +43,9 @@
         Icon(
             painter = painterResource(id = R.drawable.ic_airplanemode_active_24px),
             contentDescription = "airplane",
-            modifier = Modifier.size(24.dp).wrapContentSize(align = Alignment.Center),
+            modifier = Modifier
+                .size(ToggleButtonDefaults.DefaultIconSize)
+                .wrapContentSize(align = Alignment.Center),
         )
     }
 }
diff --git a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Button.kt b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Button.kt
index 078c5b3..241330a 100644
--- a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Button.kt
+++ b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Button.kt
@@ -27,7 +27,6 @@
 import androidx.compose.foundation.shape.CircleShape
 import androidx.compose.material.ripple.rememberRipple
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.Immutable
 import androidx.compose.runtime.Stable
 import androidx.compose.runtime.State
@@ -44,8 +43,11 @@
 /**
  * Wear Material [Button] that offers a single slot to take any content (text, icon or image).
  *
- * The [Button] is circular in shape. The recommended sizes can be obtained
- * from [ButtonDefaults].
+ * The [Button] is circular in shape. The recommended [Button] sizes can be obtained
+ * from [ButtonDefaults] - see [ButtonDefaults.DefaultButtonSize], [ButtonDefaults.LargeButtonSize],
+ * [ButtonDefaults.SmallButtonSize].
+ * Icon content should be of size [ButtonDefaults.DefaultIconSize],
+ * [ButtonDefaults.LargeIconSize] or [ButtonDefaults.SmallIconSize] respectively.
  *
  * The recommended set of [ButtonColors] styles can be obtained from [ButtonDefaults], e.g.
  * [ButtonDefaults.primaryButtonColors] to get a color scheme for a primary [Button] which by
@@ -57,6 +59,9 @@
  * Example of a [Button] displaying an icon:
  * @sample androidx.wear.compose.material.samples.ButtonWithIcon
  *
+ * Example of a large [Button] displaying an icon:
+ * @sample androidx.wear.compose.material.samples.LargeButtonWithIcon
+ *
  * Example of a [Button] with text content and size modified to LargeButtonSize:
  * @sample androidx.wear.compose.material.samples.ButtonWithText
  *
@@ -121,7 +126,7 @@
  * The [CompactButton] is circular in shape and has background size
  * [ButtonDefaults.ExtraSmallButtonSize]. There is an optional transparent padding around
  * the background, defaulted to [ButtonDefaults.CompactButtonBackgroundPadding],
- * which increases the clickable area.
+ * which increases the clickable area. Icon content should have size [ButtonDefaults.SmallIconSize].
  *
  * The recommended set of [ButtonColors] styles can be obtained from [ButtonDefaults], e.g.
  * [ButtonDefaults.primaryButtonColors] to get a color scheme for a primary [Button] which by
@@ -218,9 +223,9 @@
 /**
  * Contains the default values used by [Button].
  */
-object ButtonDefaults {
+public object ButtonDefaults {
     @Composable
-    fun primaryButtonColors(
+    public fun primaryButtonColors(
         backgroundColor: Color = MaterialTheme.colors.primary,
         contentColor: Color = contentColorFor(backgroundColor)
     ): ButtonColors {
@@ -231,7 +236,7 @@
     }
 
     @Composable
-    fun secondaryButtonColors(
+    public fun secondaryButtonColors(
         backgroundColor: Color = MaterialTheme.colors.surface,
         contentColor: Color = contentColorFor(backgroundColor)
     ): ButtonColors {
@@ -242,7 +247,7 @@
     }
 
     @Composable
-    fun iconButtonColors(
+    public fun iconButtonColors(
         contentColor: Color = MaterialTheme.colors.onSurface,
     ): ButtonColors {
         return buttonColors(
@@ -254,31 +259,46 @@
     /**
      * The default background size of a [CompactButton].
      */
-    val ExtraSmallButtonSize = 32.dp
+    public val ExtraSmallButtonSize = 32.dp
 
     /**
      * The recommended size for a small [Button].
      * You can apply this value for the size by overriding Modifier.size directly on [Button].
      */
-    val SmallButtonSize = 48.dp
+    public val SmallButtonSize = 48.dp
 
     /**
      * The default size applied for the [Button].
      * Note that you can override it by applying Modifier.size directly on [Button].
      */
-    val DefaultButtonSize = 52.dp
+    public val DefaultButtonSize = 52.dp
 
     /**
      * The recommended size for a large [Button].
      * You can apply this value for the size by overriding Modifier.size directly on [Button].
      */
-    val LargeButtonSize = 60.dp
+    public val LargeButtonSize = 60.dp
+
+    /**
+     * The size of an icon when used inside a small-sized [Button] or a [CompactButton].
+     */
+    public val SmallIconSize = 24.dp
+
+    /**
+     * The default size of an icon when used inside a default-sized [Button].
+     */
+    public val DefaultIconSize = 26.dp
+
+    /**
+     * The size of an icon when used inside a large-sized [Button].
+     */
+    public val LargeIconSize = 30.dp
 
     /**
      * The default padding for a [CompactButton]. This will result in a larger tap area
      * than visible area.
      */
-    val CompactButtonBackgroundPadding = 8.dp
+    public val CompactButtonBackgroundPadding = 8.dp
 
     /**
      * Creates a [ButtonColors] that represents the default background and content colors used in
@@ -290,7 +310,7 @@
      * @param disabledContentColor the content color of this [Button] when not enabled
      */
     @Composable
-    fun buttonColors(
+    public fun buttonColors(
         backgroundColor: Color = MaterialTheme.colors.primary,
         contentColor: Color = contentColorFor(backgroundColor),
         disabledBackgroundColor: Color = backgroundColor.copy(alpha = ContentAlpha.disabled),
diff --git a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/ToggleButton.kt b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/ToggleButton.kt
index 31474da..c57c6d7 100644
--- a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/ToggleButton.kt
+++ b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/ToggleButton.kt
@@ -25,7 +25,6 @@
 import androidx.compose.foundation.shape.CircleShape
 import androidx.compose.material.ripple.rememberRipple
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.Immutable
 import androidx.compose.runtime.Stable
 import androidx.compose.runtime.State
@@ -43,8 +42,9 @@
  * (text, icon or image).
  *
  * The [ToggleButton] is circular in shape and defaults to size
- * [ToggleButtonDefaults.DefaultToggleButtonSize].
- * Other recommended sizes can be obtained from [ToggleButtonDefaults].
+ * [ToggleButtonDefaults.DefaultToggleButtonSize] or [ToggleButtonDefaults.SmallToggleButtonSize].
+ * Icon content should be of size [ToggleButtonDefaults.DefaultIconSize] or
+ * [ToggleButtonDefaults.SmallIconSize] respectively.
  *
  * The recommended set of checked and unchecked [ToggleButtonColors] can be obtained
  * from [ToggleButtonDefaults.toggleButtonColors], which defaults to
@@ -147,18 +147,28 @@
 /**
  * Contains the default values used by [ToggleButton].
  */
-object ToggleButtonDefaults {
+public object ToggleButtonDefaults {
     /**
      * The recommended size for a small [ToggleButton].
      * You can apply this value for the size by overriding Modifier.size directly on [ToggleButton].
      */
-    val SmallToggleButtonSize = 48.dp
+    public val SmallToggleButtonSize = 48.dp
 
     /**
      * The default size applied for the [ToggleButton].
      * Note that you can override it by applying Modifier.size directly on [ToggleButton].
      */
-    val DefaultToggleButtonSize = 52.dp
+    public val DefaultToggleButtonSize = 52.dp
+
+    /**
+     * The size of an icon when used inside a small-sized [ToggleButton].
+     */
+    public val SmallIconSize = 24.dp
+
+    /**
+     * The default size of an icon when used inside a default-sized [ToggleButton].
+     */
+    public val DefaultIconSize = 26.dp
 
     /**
      * Creates a [ToggleButtonColors] that represents the background and content colors
@@ -182,7 +192,7 @@
      * and not enabled
      */
     @Composable
-    fun toggleButtonColors(
+    public fun toggleButtonColors(
         checkedBackgroundColor: Color = MaterialTheme.colors.primary,
         checkedContentColor: Color = contentColorFor(checkedBackgroundColor),
         disabledCheckedBackgroundColor: Color =
diff --git a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/MaterialDemos.kt b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/MaterialDemos.kt
index 2272460..a709b58 100644
--- a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/MaterialDemos.kt
+++ b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/MaterialDemos.kt
@@ -45,6 +45,7 @@
 import androidx.wear.compose.material.samples.InlineSliderSample
 import androidx.wear.compose.material.samples.InlineSliderSegmentedSample
 import androidx.wear.compose.material.samples.InlineSliderWithIntegerSample
+import androidx.wear.compose.material.samples.LargeButtonWithIcon
 import androidx.wear.compose.material.samples.ScalingLazyColumnEdgeAnchoredAndAnimatedScrollTo
 import androidx.wear.compose.material.samples.SimplePicker
 import androidx.wear.compose.material.samples.SimpleScaffoldWithScrollIndicator
@@ -227,6 +228,9 @@
                     "Samples",
                     listOf(
                         ComposableDemo("Button With Icon") { Centralize { ButtonWithIcon() } },
+                        ComposableDemo("Button With Large Icon") {
+                            Centralize { LargeButtonWithIcon() }
+                        },
                         ComposableDemo("Button With Text") { Centralize { ButtonWithText() } },
                         ComposableDemo("Compact Button With Icon") {
                             Centralize { CompactButtonWithIcon() }
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchState.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchState.kt
index ab97e90..c4c1133 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchState.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchState.kt
@@ -119,6 +119,7 @@
         writer.println("digitalPreviewReferenceTimeMillis=$digitalPreviewReferenceTimeMillis")
         writer.println("chinHeight=$chinHeight")
         writer.println("isHeadless=$isHeadless")
+        writer.println("watchFaceInstanceId=${watchFaceInstanceId.value}")
         writer.decreaseIndent()
     }
 }
diff --git a/webkit/OWNERS b/webkit/OWNERS
index 8ced73f..b275a9b 100644
--- a/webkit/OWNERS
+++ b/webkit/OWNERS
@@ -1,3 +1,3 @@
-# Bug component: 460423
+# Bug component: 461230
 ntfschr@google.com
 torne@google.com